1 """Beam search through a graph."""
2
4 __doc__ = """This is a node of a graph, it includes links to other nodes."""
5
6 - def __init__(self, cost=0.0, label=None, terminal=0, comment=None):
7 """Create a node, with a specified cost (used in the beam search),
8 and a label (arbitrary information).
9 Terminal nodes are marked, and terminate the search."""
10
11 self.terminal = terminal
12 self.out = []
13 self.cost = cost
14 self.label = label
15 self.comment = comment
16
17 - def add(self, nextnode, cost=0.0, label=None):
18 """Add a link from self to nextnode.
19 Links can have a cost and label, too."""
20 self.out.append( (nextnode, cost, label) )
21 return nextnode
22
23
25 return "<node term=%d label=%s nout=%d cost=%f>" % (self.terminal, str(self.label),
26 len(self.out), self.cost)
27
28 __repr__ = __str__
29
30
31
32
34 __doc__ = """Linked list."""
35
37 self.link = link
38 self.contents = contents
39
41 o = [self.contents]
42 while self.link is not None:
43 self = self.link
44 o.append(self.contents)
45 return o
46
48 o = self.walk()
49 o.reverse()
50 return o
51
52
53
54 -def go(graph, nbeam, cbeam):
55 """Search the graph for the lowest cost routes.
56 Returns [ (cost, route), ...] where cost is the cost of a route,
57 and route is [node, node, node, ...] the list of nodes on the route.
58 Routes are sorted in order of increasing cost.
59 """
60
61 assert isinstance(graph, node), "Non-node starts graph: %s" % str(graph)
62
63 current = [ (0.0, graph, _l_list(graph, None)) ]
64 terminate = [ ]
65
66 while 1:
67 nextstep = []
68 for (cost, anode, path) in current:
69 assert isinstance(anode, node), "Non-node in graph: %s" % str(anode)
70 for (nnode, c, ll) in anode.out:
71
72 newcost = cost + nnode.cost + c
73 newpath = _l_list(nnode, path)
74
75 if nnode.terminal:
76 terminate.append( ( newcost, newpath) )
77 else:
78 nextstep.append( (newcost, nnode, newpath) )
79 if len(nextstep) == 0:
80 break
81 nextstep.sort()
82 if len(nextstep) > nbeam:
83 nextstep = nextstep[:nbeam]
84 costlimit = nextstep[0][0] + cbeam
85 i = 0
86 while i < len(nextstep):
87 if nextstep[i][0] > costlimit:
88 break
89 i += 1
90 current = nextstep[:i]
91 terminate.sort()
92 if len(terminate) > nbeam:
93 terminate = terminate[:nbeam]
94 costlimit = terminate[0][0] + cbeam
95 o = []
96 for (cost, path) in terminate:
97 if cost > costlimit:
98 break
99 o.append( (cost, path.rwalk() ) )
100 return o
101
102
103
105 graph = node(0.0, "A")
106 graph.add(node(0.0, "B")).add(node(0.1, "C")).add(node(0.2, "D")).add(node(-0.3, terminal=1, label="E"))
107 graph.add(node(1.0, "a")).add(node(1, "b")).add(node(2, terminal=1, label="c"))
108 graph.add(node(0.0, label="q", terminal=1), 1.0, label="edge")
109 print go(graph, 100, 1000.0)
110
111
112 if __name__ == '__main__':
113 test()
114