def test_graph_depth_first_trivial_graph(): csgraph = np.array([[0]]) csgraph = csgraph_from_dense(csgraph, null_value=0) bfirst = np.array([[0]]) for directed in [True, False]: bfirst_test = depth_first_tree(csgraph, 0, directed) assert_array_almost_equal(csgraph_to_dense(bfirst_test), bfirst)
def test_csgraph_to_dense(): G = np.random.random((10, 10)) nulls = (G < 0.8) G[nulls] = np.inf G_csr = csgraph_from_dense(G) for null_value in [0, 10, -np.inf, np.inf]: G[nulls] = null_value assert_array_almost_equal(G, csgraph_to_dense(G_csr, null_value))
def test_graph_breadth_first(): csgraph = np.array([[0, 1, 2, 0, 0], [1, 0, 0, 0, 3], [2, 0, 0, 7, 0], [0, 0, 7, 0, 1], [0, 3, 0, 1, 0]]) csgraph = csgraph_from_dense(csgraph, null_value=0) bfirst = np.array([[0, 1, 2, 0, 0], [0, 0, 0, 0, 3], [0, 0, 0, 7, 0], [0, 0, 0, 0, 0], [0, 0, 0, 0, 0]]) for directed in [True, False]: bfirst_test = breadth_first_tree(csgraph, 0, directed) assert_array_almost_equal(csgraph_to_dense(bfirst_test), bfirst)
def test_graph_depth_first(): if csgraph_from_dense is None: raise SkipTest("Old version of scipy, doesn't have csgraph.") csgraph = np.array([[0, 1, 2, 0, 0], [1, 0, 0, 0, 3], [2, 0, 0, 7, 0], [0, 0, 7, 0, 1], [0, 3, 0, 1, 0]]) csgraph = csgraph_from_dense(csgraph, null_value=0) dfirst = np.array([[0, 1, 0, 0, 0], [0, 0, 0, 0, 3], [0, 0, 0, 0, 0], [0, 0, 7, 0, 0], [0, 0, 0, 1, 0]]) for directed in [True, False]: dfirst_test = depth_first_tree(csgraph, 0, directed) assert_array_almost_equal(csgraph_to_dense(dfirst_test), dfirst)
def test_multiple_edges(): # create a random sqare matrix with an even number of elements X = np.random.random((10, 10)) Xcsr = csr_matrix(X) # now double-up every other column Xcsr.indices[::2] = Xcsr.indices[1::2] # normal sparse toarray() will sum the duplicated edges Xdense = Xcsr.toarray() assert_array_almost_equal(Xdense[:, 1::2], X[:, ::2] + X[:, 1::2]) # csgraph_to_dense chooses the minimum of each duplicated edge Xdense = csgraph_to_dense(Xcsr) assert_array_almost_equal(Xdense[:, 1::2], np.minimum(X[:, ::2], X[:, 1::2]))
lineList=[] for i in range(nodeList.shape[0]): pt1=nodeList[i,0] pt2=nodeList[i,1] lineList.append([tri.points[pt1,:],tri.points[pt2,:]]) lines=LineCollection(lineList) fig, ax = plt.subplots(1) ax.add_collection(pc2) ax.add_collection(lines) artists = ax.scatter(nRoomCenters[0,:],nRoomCenters[1,:]) plt.show() #Now randomly add some connections back: lineAddBack=np.random.rand(tri.points.shape[0],tri.points.shape[0])<addBackChance addBack=csg.csgraph_to_dense(minSpanTree)+csg.csgraph_to_dense(roomGraph)*lineAddBack nodeList2=np.asarray(addBack.nonzero()).T #Use a LineCollection to visualize the spanning tree: lineList2=[] for i in range(nodeList2.shape[0]): pt1=nodeList2[i,0] pt2=nodeList2[i,1] lineList2.append([tri.points[pt1,:],tri.points[pt2,:]]) lines2=LineCollection(lineList2) fig, ax = plt.subplots(1) ax.add_collection(pc2b) ax.add_collection(lines2) artists = ax.scatter(nRoomCenters[0,:],nRoomCenters[1,:]) plt.show()
def solve(self): # https://de.wikipedia.org/wiki/Algorithmus_von_Christofides # 1. Get minimal spanning tree (msp) logging.debug("Compute minimal spanning tree ...") v = list(range(self.num_nodes)) graph = csgraph_from_dense(self.d) msp = minimum_spanning_tree(graph) msp_d = csgraph_to_dense(msp) # Somehow the dense graph is not undirected, which is bad for the next step ... for i in range(self.num_nodes): for j in range(self.num_nodes): if i == j: continue if msp_d[i, j] == 0: msp_d[i, j] = msp_d[j, i] # 2. Find the set of vertices with odd degree in the msp (T) logging.debug("Find vertices with odd degree in minimal spanning tree ...") odd_vertices = [i for i in v if sum([msp_d[i, j] != 0 for j in v]) % 2 == 1] # 3. Find a minimum-weight perfect matching M in the induced subgraph (isg) given by the set of odd vertices # Create adjacency matrix and take the negative values, as scipy only provides maximum weight bipartite matching logging.debug("Find maximum weight bipartite matching in subgraph induced by odd nodes induced ...") isg_d = np.zeros((self.num_nodes, self.num_nodes)) for i in range(self.num_nodes): for j in range(i+1, self.num_nodes): if i in odd_vertices and j in odd_vertices: isg_d[i, j] = -self.d[i, j] isg_d[j, i] = -self.d[i, j] isg_graph = csgraph_from_dense(isg_d) m = maximum_bipartite_matching(isg_graph) # 4. Combine edges of M and T (the msp) to form a connected multigraph H. logging.debug("Combine edges from minimal spanning tree" "and the maximum weight bipartite matching to a multigraph") msp_m_combined = networkx.MultiGraph() # Add edges from the minimum spanning tree for i in range(self.num_nodes): for j in range(i+1, self.num_nodes): if msp_d[i, j] > 0: msp_m_combined.add_edge(i, j, weight=msp_d[i, j]) # Add edges from the minimum-weight perfect matching M m_cp = m.copy() for i in m: if i >= 0 and m_cp[m[i]] >= 0: msp_m_combined.add_edge(i, m[i], weight=self.d[i, m[i]]) # Do not add the same edge twice m_cp[m[i]] = -1 m_cp[i] = -1 # 5. Find Eulerian circuit in H, starting at node 0 logging.debug("Find an eulerian path in the multigraph starting at vertice 0") eulerian_path = networkx.eulerian_path(msp_m_combined, source=0) # 6. Make the circuit found in previous step into a Hamiltonian circuit # Follow the eulerian path and replace already visited vertices by an edge to the next not yet visited vertice. logging.debug("Convert eulerarian path to hamiltonian circuit") first_edge = next(eulerian_path) self.solution = [first_edge[0], first_edge[1]] for e in eulerian_path: if e[1] in self.solution: continue else: self.solution.append(e[1]) self.dist = self.tour_to_dist(self.solution)