def test_eulerian_path_multigraph_undirected(self): G = nx.MultiGraph() result = [(2, 1), (1, 2), (2, 1), (1, 2), (2, 3), (3, 4)] G.add_edges_from(result) assert result == list(nx.eulerian_path(G)) assert result == list(nx.eulerian_path(G, source=2)) with pytest.raises(nx.NetworkXError): list(nx.eulerian_path(G, source=3)) with pytest.raises(nx.NetworkXError): list(nx.eulerian_path(G, source=1))
def test_eulerian_path_eulerian_circuit(self): G = nx.DiGraph() result = [(1, 2), (2, 3), (3, 4), (4, 1)] result2 = [(2, 3), (3, 4), (4, 1), (1, 2)] result3 = [(3, 4), (4, 1), (1, 2), (2, 3)] G.add_edges_from(result) assert result == list(nx.eulerian_path(G)) assert result == list(nx.eulerian_path(G, source=1)) assert result2 == list(nx.eulerian_path(G, source=2)) assert result3 == list(nx.eulerian_path(G, source=3))
def test_eulerian_path_undirected(self): G = nx.Graph() result = [(1, 2), (2, 3), (3, 4), (4, 5)] result2 = [(5, 4), (4, 3), (3, 2), (2, 1)] G.add_edges_from(result) assert list(nx.eulerian_path(G)) in (result, result2) assert result == list(nx.eulerian_path(G, source=1)) assert result2 == list(nx.eulerian_path(G, source=5)) with pytest.raises(nx.NetworkXError): list(nx.eulerian_path(G, source=3)) with pytest.raises(nx.NetworkXError): list(nx.eulerian_path(G, source=2))
def test_eulerian_path_straight_link(self): G = nx.DiGraph() result = [(1, 2), (2, 3), (3, 4), (4, 5)] G.add_edges_from(result) assert result == list(nx.eulerian_path(G)) assert result == list(nx.eulerian_path(G, source=1)) with pytest.raises(nx.NetworkXError): list(nx.eulerian_path(G, source=3)) with pytest.raises(nx.NetworkXError): list(nx.eulerian_path(G, source=4)) with pytest.raises(nx.NetworkXError): list(nx.eulerian_path(G, source=5))
def Christofides(adj_matrix): G = nx.from_numpy_array(adj_matrix) mst_G = nx.minimum_spanning_tree(G) odd_deg_nodes = [node for (node, val) in mst_G.degree() if val%2==1 ] odd_deg_nodes_ix = np.ix_(odd_deg_nodes, odd_deg_nodes) G_ = nx.from_numpy_array(-1 * adj_matrix[odd_deg_nodes_ix]) min_weight_matching = nx.max_weight_matching(G_, maxcardinality=True) min_weight_matching_edges = [edge for edge in min_weight_matching] euler_multigraph = nx.MultiGraph(mst_G) for edge in min_weight_matching: euler_multigraph.add_edge(odd_deg_nodes[edge[0]], odd_deg_nodes[edge[1]], weight=adj_matrix[odd_deg_nodes[edge[0]]][odd_deg_nodes[edge[1]]]) # nx.draw(euler_multigraph, with_labels=True, font_weight='bold') # plt.show() multi_visit_nodes = [node for (node, val) in euler_multigraph.degree() if val>2 ] eulerian_walk = nx.eulerian_path(euler_multigraph) eulerian_walk_nodes = [v for (u,v) in nx.eulerian_path(euler_multigraph)] for node in multi_visit_nodes: indices = [i for i, x in enumerate(eulerian_walk_nodes) if x == node] for j in range(1, len(indices)): del eulerian_walk_nodes[indices[j]] tsp_tour_cost = 0 tsp_tour = nx.DiGraph() for i in range(len(eulerian_walk_nodes)): if i<len(eulerian_walk_nodes)-1: tsp_tour.add_edge(eulerian_walk_nodes[i],eulerian_walk_nodes[i+1],weight = adj_matrix[eulerian_walk_nodes[i], eulerian_walk_nodes[i+1]]) tsp_tour_cost = tsp_tour_cost + adj_matrix[eulerian_walk_nodes[i], eulerian_walk_nodes[i+1]] else: tsp_tour.add_edge(eulerian_walk_nodes[i],eulerian_walk_nodes[0],weight = adj_matrix[eulerian_walk_nodes[i], eulerian_walk_nodes[0]]) tsp_tour_cost = tsp_tour_cost + adj_matrix[eulerian_walk_nodes[i], eulerian_walk_nodes[0]] return tsp_tour, eulerian_walk_nodes, tsp_tour_cost
def test_eulerian_path(self): x = [(4, 0), (0, 1), (1, 2), (2, 0)] for e1, e2 in zip(x, nx.eulerian_path(nx.DiGraph(x))): assert e1 == e2
print(hierholzer(toy)) ''' Eulerian Path test i made the test on R and on a toy example because eulerian path on graph of provinces dosen't exist because the graph of provinces in not strongly connected''' time_eulerian = [] time_eulerian_nx = [] num_node = [3, 9, 19, 29, 39, 49] for test in range(len(num_node)): toy = nx.complete_graph(num_node[test]) toy = nx.eulerize(toy) # print(nx.is_eulerian(toy)) # print(nx.has_eulerian_path(toy)) # print(nx.has_eulerian_path(R)) # print(nx.has_eulerian_path(P)) '''EULERIAN PATH ON A TOY EXAMPLE USING NETWORKX''' start = time.time() path = nx.eulerian_path(toy) # print(list(path)) end = time.time() time_eulerian_nx.append(end - start) '''EULERIAN PATH ON A TOY EXAMPLE USING IMPLEMENTED FUNCTIONS''' start = time.time() path = hierholzer(toy) # print(path) end = time.time() time_eulerian.append(end - start) print("\nTime for calculate eulerian path") print("Number of graph vertex", num_node) print("Using NetworkX Function on Toy example: ", time_eulerian_nx) print("Using simple implementation on Toy example: ", time_eulerian) plt.plot(num_node, time_eulerian, label='Implemented')
def importar_texto_2(self): options = QFileDialog.Options() fileName, _ = QFileDialog.getOpenFileName(self.MainWindow, "Elige ejemplo a importar", "", "Text Files (*.txt)", options=options) if fileName: self.text_path_2.setText(str(fileName)) archivo = open(fileName, encoding="utf8") texto = archivo.read() #creando bigramas texto_limpio = limpiar_texto(texto) bigramas_texto = [] bigramas_texto = crear_bigramas(texto_limpio) #obteniendo tags partag = [] partag = crear_tags(texto_limpio) auxpartag = partag[:] g = nx.Graph() for a, b in bigramas_texto: subtag = partag.pop(0) g.add_edge(str(a), str(b), label=subtag) #empieza analisis de isomorfismo nodos = get_nodes(g) self.no_nodos_2.setText(str(nodos)) arcos = get_edges(g) self.no_arcos_2.setText(str(arcos)) grados = get_degree(g) self.grados_2.setText(str(grados)) if (nx.is_eulerian(g)): self.eulerian_2.setText(str(nx.eulerian_circuit(g))) else: self.eulerian_2.setText("No tiene circuito") if (nx.has_eulerian_path(g)): lista = list(nx.eulerian_path(g)) self.eulerian_path_2.setText(str(lista)) else: self.eulerian_path_2.setText("No tiene camino") openl = [] closedl = [] visitados = [] inicial = [texto_limpio[0]] terminal = texto_limpio[len(texto_limpio) - 1] arcos = subtag nodos = texto_limpio while inicial: openl.append(inicial.pop(0)) while openl: elem = openl.pop(0) closedl.append(elem) #print("New closed:") #print(closedl) for tup in arcos: if tup[0] == elem: if tup[1] not in closedl and tup[1] not in openl: openl.append(tup[1]) print(openl) #print("---------------------") visitados = visitados + closedl novisitados = list(set(nodos) - set(visitados)) #print("Nodos no visitados") #print(novisitados) if (novisitados): self.conexo_2.setText("No es conexo") else: self.conexo_2.setText("Si es conexo") numero_cromatico = coloreo_grafos(g, auxpartag, bigramas_texto, 'grafo2.gv') self.cromatico_2.setText(str(numero_cromatico)) archivo.close()
#!usr/bin/python import GenerateReads.generateGenome as gen import MakeDeBrujn.deBrujn as graph import GenerateReads.sampleReads as samp import Reconstruct.reconstruct as rec import networkx as nx import matplotlib.pyplot as plt genome = gen.literGen('Shakespeare1.txt', 70) kmers = samp.kmerSamples(genome, 25, 0.01, 250) deB = graph.deBrujn(kmers, 0.01) print(nx.is_connected(deB.to_undirected()), "\n") print(nx.adjacency_matrix(deB)) eu = nx.eulerian_path(deB) euler = list(eu) # print(euler) # print(euler[0]) # print(euler[0][0]) # print(euler[0][0][0]) genRec = rec.naiveReconstruct(euler) print(genome, "\n") print(genRec)
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)
def _order_edges_in_block(self, block_data, drop_augmented): """Produce an edge sequence for all edges in the component. Parameters ---------- block_data : pandas.DataFrame A DataFrame representing all the edges within a single block. drop_augmented : bool Whether or not to keep any edges that needed to be added to the source edges in order to navigate the network. Returns ------- edges : pandas.DataFrame The same edges that were input with the edge order and route type as new columns. """ logger.debug("order_edges_by_block started") logger.debug("Received edge data of shape %s", block_data.shape) # Sort the DataFrame to load right hand arcs into NetworkX first. # Note that Eulerian paths work in reverse order. block_data = block_data.sort_values(self.leftrightflag_field, ascending=False) block_g = nx.from_pandas_edgelist(block_data, source=self.source_field, target=self.target_field, edge_attr=True, create_using=self.graph_type) logger.debug("Block contains %s edges and %s nodes", block_g.number_of_edges(), block_g.number_of_nodes()) # if the graph is empty it means there is a problem with the source data # an error is logged, but other blocks are still processed if nx.is_empty(block_g): logger.error("Block contains no edges and cannot be sequenced") return # Scale nodes that are mid-segment by looking for duplicated ngd_str_uid values logger.debug( "Looking for nodes that fall in the middle of a road segment") block_data['same_ngd_str_uid'] = block_data.duplicated( subset=[self.struid_field], keep=False) mid_arc_start_nodes = set( block_data.loc[block_data['same_ngd_str_uid'] == True, self.source_field]) mid_arc_end_nodes = set( block_data.loc[block_data['same_ngd_str_uid'] == True, self.target_field]) mid_arc_nodes = mid_arc_start_nodes.intersection(mid_arc_end_nodes) if mid_arc_nodes: logger.debug("Found mid-segment nodes: %s", mid_arc_nodes) self._apply_node_scaling_factor(mid_arc_nodes, factor=-0.5) # initialize the edge sequence counter edge_sequence = 0 # record what type of path was used to determine the circuit path_indicator_name = self.path_indicator path_indicator_edges = {} # blocks don't necessarily form fully connected graphs, so cycle through the components logger.debug("Block contains %s connected components", nx.number_weakly_connected_components(block_g)) for block_comp in sorted(nx.weakly_connected_components(block_g), key=len, reverse=True): logger.debug( "Creating subgraph from connected component with %s nodes", len(block_comp)) block_g_comp = block_g.subgraph(block_comp) # determine the preferred start node for this block component preferred_sp = self._get_preferred_start_node(block_g_comp.nodes) logger.debug("Preferred start node for this block: %s", preferred_sp) logger.debug("Component contains %s edges and %s nodes", block_g_comp.number_of_edges(), len(block_g_comp)) # Need to pick an approach to processing this component depending on what type of circuit it forms. # Ideally things are a Eulerian circuit that can be walked and return to start, but not all blocks form # these nice circuits. If no good circuit can be found, then sequence numbers are just applied but may # not form a logical order. # Track the sequence value in case the enumeration method needs to be reset. This gets used when using # the preferred start point fails, and also controls if the start node for this component is marked as a # point we want to cluster on. seq_val_at_start = edge_sequence # the preferred option is a Eulerian circuit, so try that first # logger.debug("Available edges: %s", block_g_comp.edges) if nx.is_eulerian(block_g_comp): logger.debug("Block component is eulerian.") # record all these edges as being eulerian indicator = dict( zip(block_g_comp.edges, ['circuit'] * block_g_comp.size())) path_indicator_edges.update(indicator) # enumerate the edges and order them directly logger.debug("Creating Eulerian circuit from node %s", preferred_sp) for u, v, k in nx.eulerian_circuit(block_g_comp, source=preferred_sp, keys=True): edge_sequence += 1 block_g.edges[u, v, k][self.eo_name] = edge_sequence # logger.debug("Sequence applied: (%s, %s, %s) = %s", u, v, k, edge_sequence) # next best option is a path that stops at a different location from the start point elif nx.has_eulerian_path(block_g_comp): logger.debug("Block component forms Eulerian path") # record all these edges as being a eulerian path indicator = dict( zip(block_g_comp.edges, ['path'] * block_g_comp.size())) path_indicator_edges.update(indicator) try: logger.debug( "Trying to create path from preferred start node %s", preferred_sp) for u, v, k in nx.eulerian_path(block_g_comp, source=preferred_sp, keys=True): edge_sequence += 1 # check if the start point is actually in the first edge if edge_sequence == 1 and not (preferred_sp == u or preferred_sp == v): logger.debug( "Preferred start point not present on starting edge, throwing KeyError." ) raise KeyError("Invalid starting edge") # Sometimes the preferred start point means walking over the same edge twice, which will leave # a data gap (the previous edge order value will be overwritten). If this happens, throw a # KeyError if block_g.edges[u, v, k].get(self.eo_name): logger.debug("Edge already sequenced.") raise KeyError( "Preferred start point results in backtracking." ) block_g.edges[u, v, k][self.eo_name] = edge_sequence # logger.debug("Sequence applied: (%s, %s, %s) = %s", u, v, k, edge_sequence) if edge_sequence < block_g_comp.number_of_edges(): logger.debug("It looks like some edges got missed") raise KeyError("Missing edges on path") logger.debug( "Path was created from desired start point %s", preferred_sp) except KeyError: # preferred start point failed; let networkx pick and start over logger.debug( "Preferred start node did not create a path. Trying a different one." ) # reset the path listing since a new point will be picked logger.debug("Reset edge_sequence value to %s", seq_val_at_start) edge_sequence = seq_val_at_start for u, v, k in nx.eulerian_path(block_g_comp, keys=True): edge_sequence += 1 block_g.edges[u, v, k][self.eo_name] = edge_sequence # logger.debug("Sequence applied: (%s, %s, %s) = %s", u, v, k, edge_sequence) # No good path exists, which means someone will have to backtrack else: logger.debug( "Non-eulerian block is not easily traversable. Eulerizing it." ) # Record all these edges as being augmented. indicator = dict( zip(block_g_comp.edges, ['augmented'] * block_g_comp.size())) path_indicator_edges.update(indicator) # Send this data to the anomaly folder so that it can be investigated later. It could have addressable # issues that operations can correct for the next run. logger.debug("Writing anomaly set for this block") bf_uid_set = list( nx.get_edge_attributes( block_g_comp, self.edge_uid_field).values()).pop() anomaly_file_name = f"anomaly_block_component.{bf_uid_set}.yaml" nx.write_yaml(block_g_comp, (self.anomaly_folder / anomaly_file_name).as_posix()) # You cannot eulerize a directed graph, so create an undirected one logger.debug("Creating MultiGraph from directed graph.") temp_graph = nx.MultiGraph() for u, v, data in block_g_comp.edges(data=True): key = temp_graph.add_edge(u, v, **data) # logger.debug("Adding edge (%s, %s, %s) to temporary graph.", u, v, key) logger.debug("Created temporary MultiGraph with %s edges", temp_graph.number_of_edges()) # Convert the temporary graph to a proper Euler circuit so that it can be traversed. logger.debug("Eulerizing MultiGraph") euler_block = nx.eulerize(temp_graph) logger.debug("Added %s edges to the block", (euler_block.size() - temp_graph.size())) logger.debug("Number of vertices in eulerized graph: %s", euler_block.number_of_nodes()) # As we try to traverse the undirected graph, we need to keep track of places already visited to make # sure arcs are not skipped. visited_edges = Counter() # augmented edges will throw the node weights off, so don't bother trying the preferred start node logger.debug("Generating path through augmented block") for u, v, k in nx.eulerian_circuit(euler_block, preferred_sp, keys=True): # augmented edges have no attributes, so look for one and skip the edge if nothing is returned if drop_augmented and not euler_block.edges[u, v, k].get( self.edge_uid_field): logger.debug("Ignoring augmented edge (%s, %s, %s)", u, v, k) continue # Increment the sequence value for each edge we see. edge_sequence += 1 # Since we formed an undirected MultiGraph we need to check the orientation of the nodes on the # edge to assign the sequence back to the directed graph. start_node = u end_node = v available_edge_count = block_g.number_of_edges( start_node, end_node) # If no edges exist, invert the nodes and check again. # This also checks to see if we've already encountered all the edges between these nodes, indicating # we need to process the inverse of the start and end values if available_edge_count == 0 or ( ((start_node, end_node) in visited_edges) and (available_edge_count == visited_edges[(start_node, end_node)])): logger.debug( "Nothing to process between (%s, %s), inverting nodes.", start_node, end_node) start_node = v end_node = u available_edge_count = block_g.number_of_edges( start_node, end_node) logger.debug( "Number of edges available between (%s, %s): %s", start_node, end_node, available_edge_count) # Apply the edge_sequence to the first edge that hasn't received one yet for ki in range(available_edge_count): if not block_g.edges[start_node, end_node, ki].get( self.eo_name): logger.debug( "Edge sequence applied: (%s, %s, %s) = %s", start_node, end_node, ki, edge_sequence) block_g.edges[start_node, end_node, ki][self.eo_name] = edge_sequence visited_edges[(start_node, end_node)] += 1 break # At this point every edge should be accounted for, but in case something somehow slips through the cracks # it needs to be given a sequence label. The label almost certainly won't make much sense in terms of a # logical ordering, but this is just trying ot make sure it is counted. logger.debug("Looking for any missed edges in block component") for u, v, k in block_g_comp.edges: if not block_g.edges[u, v, k].get(self.eo_name): edge_sequence += 1 block_g.edges[u, v, k][self.eo_name] = edge_sequence logger.warning( "Applied out of order sequence to component edge (%s, %s, %s): %s", u, v, k, edge_sequence) # just log the last sequence value to make tracing easier logger.debug("Final edge sequence value for component: %s", edge_sequence) # apply a sequence value to all the edges that were discovered logger.debug("Edge order results: %s", nx.get_edge_attributes(block_g_comp, self.eo_name)) # To help cluster the start nodes, mark which node was used as the start point in this block if seq_val_at_start == 1: self._mark_chosen_start_node(block_g_comp, preferred_sp) logger.debug("Finished processing component") # record that block processing is finished logger.debug("Block processing complete") # nx.set_edge_attributes(block_g, block_sequence_labels, self.eo_name) nx.set_edge_attributes(block_g, path_indicator_edges, path_indicator_name) # check to see if the counts line up if not block_g.number_of_edges() == edge_sequence: logger.debug( "Edge sequence (%s) and edge count (%s) do not match in block", edge_sequence, block_g.number_of_edges()) # help start point clustering by apply a scaling factor to all nodes that were touched logger.debug( "Applying scaling factor to nodes in this block, except start point" ) nodes_in_block = set(block_g.nodes()) nodes_in_block.remove( preferred_sp) # don't scale the preferred start point self._apply_node_scaling_factor(nodes_in_block) logger.debug("Final node data for block: %s", self.graph.subgraph(block_g.nodes).nodes(data=True)) logger.debug("Returning pandas DataFrame from block graph.") return nx.to_pandas_edgelist(block_g, source=self.source_field, target=self.target_field)
# -*- coding: utf-8 -*- """ Created on Mon Mar 23 15:56:06 2020 @author: tanup """ import networkx as nx s = {'ATG', 'TGC', 'GTG', 'TGG', 'GGC', 'GCA', 'GCG', 'CGT'} G = nx.DiGraph() B = [] for a in s: G.add_edge(a[:2], a[-2:]) nx.draw_networkx(G, with_labels=True, pos=nx.circular_layout(G), node_color='white') C = list(nx.eulerian_path(G)) result = '' for a in C: result = result + a[0][0] result = result + C[-1][1] print(result)