class GraphOntoRectangularGrid(object): """ Locations in the grid are tuples (x, y), 0-based. Each vertex maps to one grid location. It is possible for a grid location to map to multiple vertices. """ def __init__(self, width, height=None): height = height or width assert width > 0 and height > 0 self._width = width self._height = height self._graph = SimpleGraph() self._locationMap = {} # vertex : location for x, y in product(range(self._width), range(self._height)): v = self.pushVertex((x, y)) if x > 0: self._graph.addEdge(v, self.singleVertexAt((x - 1, y))) if y > 0: self._graph.addEdge(v, self.singleVertexAt((x, y - 1))) @property def width(self): return self._width @property def height(self): return self._height @property def graph(self): return self._graph.asReadOnly() def getLocationMap(self): return dict((v, tuple(c)) for v, c in self._locationMap.items()) def pushVertex(self, xy): x, y = xy assert 0 <= x < self._width assert 0 <= y < self._height v = self._graph.pushVertex() self._locationMap[v] = xy return v def removeVertex(self, v): self._graph.removeVertex(v) del self._locationMap[v] def removeVertexAt(self, xy): self.removeVertex(self.singleVertexAt(xy)) def addEdge(self, v1, v2): self._graph.addEdge(v1, v2) def removeUniqueEdge(self, xy1, xy2): v1, v2 = map(self.singleVertexAt, (xy1, xy2)) self._graph.removeEdge(v1, v2) def verticesAt(self, xy): """Get the set of any/all vertices mapped to location.""" return set(k for k, v in self._locationMap.items() if v == xy) def singleVertexAt(self, xy): """Get the single vertex mapped to location. Error if not 1-to-1.""" v = self.verticesAt(xy) assert len(v) == 1 return v.pop() def adjacenciesAt(self, xy): return self._graph.adjacencies(self.singleVertexAt(xy)) def orthogonalAdjacencies(self, xy): x, y = xy a = self.adjacenciesAt(xy) xa = a & (self.verticesAt((x - 1, y)) | self.verticesAt((x + 1, y))) ya = a & (self.verticesAt((x, y - 1)) | self.verticesAt((x, y + 1))) return xa, ya
def _testGraph(): g = SimpleGraph() assert isinstance(g, QueryableSimpleGraph) assert not isinstance(g.asReadOnly(), SimpleGraph) assert isinstance(g.asReadOnly(), QueryableSimpleGraph) g.assertSimple() verts = [] for i in range(10): verts.append(g.pushVertex()) try: g.addVertex(verts[0]) raise AssertionError except ValueError: pass assert len(set(verts)) == len(verts) for v in verts: assert not g.adjacencies(v) assert g.connectedComponent(v) == {v} assert not g.isSeparator(v) assert g.isConnectedSet([v]) parts = g.disjointPartitions() assert len(parts) == len(verts) s = set() for p in parts: assert len(p) == 1 s.add(p.pop()) assert s == set(verts) edgeCount = 0 assert g.edgeCount() == edgeCount assert g.edgeCount(without=set()) == g.edgeCount() for i in range(len(verts) - 1): edgeCount += 1 g.addEdge(verts[i], verts[i + 1]) for v in verts[i + 2:]: assert not g.connected(verts[i + 1], v) assert not g.connected(v, verts[i + 1]) for j in range(i + 1): if j > 0: assert g.connected(verts[0], verts[j]) assert g.connected(verts[j], verts[0]) assert g.connectedComponent(verts[j]) == set(verts[:i + 2]) assert g.edgeCount() == edgeCount assert g.edgeCount(without=set()) == g.edgeCount() assert g.edgeCount(mask=verts) == g.edgeCount() for v in verts: assert g.edgeCount(mask={v}) == 0 assert g.connectedComponent(v) == set(verts) assert g.isConnectedSet(verts) assert g.isConnectedSet([verts[2], verts[4]], verts[2:5]) assert not g.isConnectedSet([verts[2], verts[4]], [verts[2], verts[4]]) assert g.isConnectedSet(verts[:4] + verts[5:]) assert not g.isConnectedSet(verts[:4] + verts[5:], verts[:4] + verts[5:]) assert not g.isSeparator(verts[0]) assert not g.isSeparator(verts[-1]) assert all(g.isSeparator(v) for v in verts[1:-1]) assert g.shortestPath(verts[0], verts[-1]) == verts assert g.shortestPath(verts[-1], verts[0]) == list(reversed(verts)) assert g.shortestPath(verts[3], verts[3]) == [verts[3]] shortcut = g.pushVertex() edgeCount += 2 g.addEdge(verts[0], shortcut) g.addEdge(verts[-1], shortcut) assert g.shortestPath(verts[0], verts[-1]) == \ [verts[0], shortcut, verts[-1]] assert verts[0] == 0 edgeCount -= 1 g.removeEdge(verts[-1], shortcut) assert g.shortestPath(shortcut, verts[1]) == [shortcut, verts[0], verts[1]] edgeCount -= len(g.adjacencies(shortcut)) assert g.edgeCount(without={shortcut}) == edgeCount g.removeVertex(shortcut) assert g.edgeCount() == edgeCount # noinspection PyUnusedLocal edgeCount = None g.addEdge(verts[0], verts[-1]) assert g.shortestPath(verts[0], verts[-1]) == [verts[0], verts[-1]] assert not any(g.isSeparator(v) for v in verts) g.removeEdge(verts[0], verts[-1]) g.asReadOnly() mask = verts[:2] + verts[3:] parts = g.disjointPartitions(mask) assert len(parts) == 2 assert not parts[0].intersection(parts[1]) assert g.isConnectedSet(parts[0]) assert g.isConnectedSet(parts[1]) for v1 in parts[0]: for v2 in parts[1]: assert g.shortestPath(v1, v2, mask) == [] assert not g.isConnectedSet(mask, mask) assert verts[2] not in parts[0].union(parts[1]) assert parts[0].union(parts[1]) == set(verts) - set(verts[2:3]) drops = [verts[i] for i in [2, 5, 8]] for v in drops: g.removeVertex(v) verts.remove(v) assert not g.adjacencies(verts[-1]) for v in verts[:-1]: assert len(g.adjacencies(v)) == 1 assert not g.isSeparator(v) g.assertSimple() assert len(g.disjointPartitions()) == 4 assert len(g.disjointPartitions(verts[1:])) == 4 assert len(g.disjointPartitions(verts[2:])) == 3 assert g.connectedComponent(verts[-1]) == set(verts[-1:]) assert g.connectedComponent(verts[1], verts[1:]) == set(verts[1:2]) backbone = [list(p)[0] for p in g.disjointPartitions()] for i in range(len(backbone) - 1): g.addEdge(backbone[i], backbone[i + 1]) g.addEdge(backbone[0], backbone[-1]) assert len(g.disjointPartitions()) == 1 assert g.connectedComponent(verts[0]) == set(verts) assert g.connected(verts[0], verts[-1]) assert g.isConnectedSet(set(verts[:1] + verts[-1:])) assert g.edgeCount(without={3, 6}) == g.edgeCount() - 5 assert g.edgeCount(without={0, 9}) == g.edgeCount() - 4