def test_graph_augmentation(): # a b c # | | | # n0 n1 n2 # |\/|\/| # |/\|/\| # n3 n4 n5 # | | | # d e f a, b, c, d, e, f = Wire('a'), Wire('b'), Wire('c'), Wire('d'), Wire( 'e'), Wire('f') n0, n1, n2, n3, n4, n5 = Node('0'), Node('1'), Node('2'), Node('3'), Node( '4'), Node('5') e0, e1, e2, e3, e4, e5, e6, e7, e8, e9, e10, e11, e12, e13 = \ Edge('0', a, n0), Edge('1', b, n1), Edge('2', c, n2), \ Edge('3', n0, n3), Edge('4', n0, n4), \ Edge('5', n1, n3), Edge('6', n1, n4), Edge('7', n1, n5), \ Edge('8', n2, n4), Edge('9', n2, n5), \ Edge('10', d, n3), Edge('11', e, n4), Edge('12', f, n5), \ Edge('13', n0, n0) inputs = [a, b, c] outputs = [d, e, f] nodes = [n0, n1, n2, n3, n4, n5] edges = [e0, e1, e2, e3, e4, e5, e6, e7, e8, e9, e10, e11, e12, e13] graph = Graph(nodes, edges, inputs, outputs) inputs_graph = Graph(inputs=inputs, nodes=[n0], edges=[e0, e13]) inputs_graph.augment(graph) expected_result = Graph([n0, n1, n2, n3, n4], [e0, e1, e2, e3, e4, e5, e6, e8, e13], inputs, []) assert inputs_graph == expected_result
def connected_graphs_split(graph: Graph) -> (Graph, Graph): """ If possible, splits a graph in two separate graphs, the first one has to be connected. Args: graph (Graph): graph to be split in two Returns: (Graph, Graph): a connected graph and the rest of the edges, nodes and so on ... """ # possible improvement, return balanced graphs if graph.nodes: increasing_graph = Graph(nodes=[graph.nodes[0]]) # the potential loops must be included edges = [ edge for edge in graph.edges if edge.n1 == graph.nodes[0] and edge.n2 == graph.nodes[0] ] increasing_graph.edges = edges elif graph.inputs: increasing_graph = Graph(inputs=[graph.inputs[0]]) elif graph.outputs: increasing_graph = Graph(outputs=[graph.outputs[0]]) else: # graph empty return Graph([], [], [], []), deepcopy(graph) while increasing_graph.augment(graph): pass leftover = graph - increasing_graph return increasing_graph, leftover
def split_and_reunite(graph: Graph) -> GenericMatrix: """Recursive function taking in a graph and returning the corresponding matrix. To do so, split the graph in two, passes the two halves to it's next iteration and reunite the two matrix obtained using the :ref:`fusion_matrices <fusion_matrices>` method from :ref:`divide_conquer`. The main part of this function is converting the graph format to the matrix format. tmp Args: graph (Graph): diagram considered Returns: GenericMatrix: matrix corresponding to the given diagram """ if len(graph.nodes) == 0: return no_node_matrix(graph.edges, graph.inputs, graph.outputs) elif len(graph.nodes) == 1 and not no_node_edges_detection(graph.edges): try: return UsedFragment.node_to_matrix(graph.nodes[0], len(graph.inputs), len(graph.outputs)) except AttributeError: return fallback_node_to_matrix(graph.nodes[0], len(graph.inputs), len(graph.outputs)) else: graph1, graph2 = connected_graphs_split(graph) if not graph2: # we rewrite graph1 and graph2 so they contain two parts of the current graph1 if no_node_edges_detection(graph.edges): # probably dead code since if a graph has such an edge (containing no node, only I/O), and has nodes, # the connected_graphs_split function would return two distinct graphs # # degenerate cases, when a graph contains only wires # in this case, graph1 will contain the I/O connected to another I/O and graph2 will contain the rest graph2 = Graph(nodes=graph.nodes) graph2 += filter_edges_inputs_outputs_by_nodes( graph2.nodes, graph) graph1 = graph - graph2 else: if graph.inputs: graph1 = Graph(inputs=[graph.inputs[0]]) graph1.augment(graph) elif graph.nodes: graph1 = Graph(nodes=[graph.nodes[0]]) else: raise RuntimeError( 'A graph with no node shouldn\'t enter in this branch') graph1 += graph1.neighbouring_i_o(graph) graph2 = graph - graph1 in_between_edges = between_graphs_edges(graph1, graph2, graph) graph1.edges += in_between_edges in_between_wires = [] for edge in in_between_edges: in_between_wires.append(Wire(edge.name)) graph1.outputs += in_between_wires graph2.inputs += in_between_wires first_half_matrix = split_and_reunite(graph1) second_half_matrix = split_and_reunite(graph2) inter_matrix_link = matrix_linker(graph1, graph2) else: first_half_matrix = split_and_reunite(graph1) second_half_matrix = split_and_reunite(graph2) inter_matrix_link = [] input_connections = wires_to_connection_point_node_sorted( graph.inputs, graph.edges, graph1.nodes, graph2.nodes, False) output_connections = wires_to_connection_point_node_sorted( graph.outputs, graph.edges, graph1.nodes, graph2.nodes, True) return divide_conquer.fusion_matrices(first_half_matrix, second_half_matrix, input_connections, output_connections, inter_matrix_link)