def filter_edges_inputs_outputs_by_nodes_negative( nodes: List[Node], containing_graph: Graph) -> Graph: """ Since the node list is split in two, many edges and wires don't need to be considered for the next iteration of each half. Thus, the edges, inputs and outputs are filtered so they are not passed to the next iteration if they are in relation with the given nodes from the other half Args: nodes (List[Node]): nodes to apply the filter from containing_graph (Graph): graph to be filtered Returns: Graph: graph containing the edges, inputs and outputs without the members in relation with the given nodes """ new_edges = containing_graph.edges[:] new_inputs = containing_graph.inputs[:] new_outputs = containing_graph.outputs[:] for edge in containing_graph.edges: if set(edge).intersection( nodes): # edge doesn't contain any node from the list new_edges.remove(edge) if set(edge).intersection(containing_graph.inputs): new_inputs.remove( list(set(edge).intersection(containing_graph.inputs))[0]) if Wire(edge.name) in containing_graph.inputs: new_inputs.remove(Wire(edge.name)) if set(edge).intersection(containing_graph.outputs): new_outputs.remove( list(set(edge).intersection(containing_graph.outputs))[0]) if Wire(edge.name) in containing_graph.outputs: new_outputs.remove(Wire(edge.name)) return Graph(edges=new_edges, inputs=new_inputs, outputs=new_outputs)
def test_between_graphs_edges(): b0 = Wire("b0") b1 = Wire("b1") b2 = Wire("b2") b3 = Wire("b3") v0 = Node("v0", 0.0, "X", 3) v1 = Node("v1", 0.0, "X", 3) v2 = Node("v2", 0.0, arity=3) v3 = Node("v3", 0.0, arity=3) e0 = Edge("e0", v0, v3) e1 = Edge("e1", v1, v2) e2 = Edge("e2", v2, v0) e3 = Edge("e3", v1, v3) e4 = Edge("e4", v0, b1) e5 = Edge("e5", v2, b0) e7 = Edge("e7", v1, b2) e8 = Edge("e8", v3, b3) inputs = [b1, b2] outputs = [b0, b3] nodes = [v3, v1, v0, v2] edges = [e3, e5, e0, e4, e2, e8, e1, e7] graph = Graph(nodes, edges, inputs, outputs) g1 = Graph([v0], [e4], [b1]) g2 = Graph([v3, v1, v2], [e2, e0, e1, e3, e7, e8, e5], [b2], [b0, b3]) result = qf.between_graphs_edges(g1, g2, graph) expected_result = [e2, e0] assert set(result) - set(expected_result) == set()
def test_split_and_reunite(): b0 = Wire("b0") b1 = Wire("b1") b2 = Wire("b2") b3 = Wire("b3") v0 = Node("v0", 0.0, "X", 3) v1 = Node("v1", 0.0, "X", 3) v2 = Node("v2", 0.0, arity=3) v3 = Node("v3", 0.0, arity=3) e0 = Edge("e0", v0, v3) e1 = Edge("e1", v1, v2) e2 = Edge("e2", v2, v0) e3 = Edge("e3", v1, v3) e4 = Edge("e4", v0, b1) e5 = Edge("e5", v2, b0) e7 = Edge("e7", v1, b2) e8 = Edge("e8", v3, b3) inputs = [b1, b2] outputs = [b0, b3] nodes = [v3, v1, v0, v2] edges = [e3, e5, e0, e4, e2, e8, e1, e7] graph = Graph(nodes, edges, inputs, outputs) m1 = [[32, 0, 0, 0], [0, 0, 0, 32], [0, 0, 0, 32], [32, 0, 0, 0]] m2 = np.zeros((4, 4)) m3 = np.zeros((4, 4)) m4 = np.zeros((4, 4)) expected_result = Pi4Matrix(m1, m2, m3, m4, 6) assert not (qf.split_and_reunite(graph) - expected_result).any()
def test_connected_graphs_split(): # a b c # | | | # n0 n1 n2 g # |\/|\/| | # |/\|/\| h # n3 n4 n5 # | | | # d e f # # problem if an edge has the same name as a node a, b, c, d, e, f = Wire('a'), Wire('b'), Wire('c'), Wire('d'), Wire( 'e'), Wire('f') g, h = Wire('g'), Wire('h') n0, n1, n2, n3, n4, n5 = Node('n0'), Node('n1'), Node('n2'), Node( 'n3'), Node('n4'), Node('n5') e0, e1, e2, e3, e4, e5, e6, e7, e8, e9, e10, e11, e12, e13 = \ Edge('e0', a, n0), Edge('e1', b, n1), Edge('e2', c, n2), \ Edge('e3', n0, n3), Edge('e4', n0, n4), \ Edge('e5', n1, n3), Edge('e6', n1, n4), Edge('e7', n1, n5), \ Edge('e8', n2, n4), Edge('e9', n2, n5), \ Edge('e10', d, n3), Edge('e11', e, n4), Edge('e12', f, n5), \ Edge('e13', n0, n0) e14 = Edge('e14', g, h) inputs = [a, b, c, g] outputs = [d, e, f, h] nodes = [n0, n1, n2, n3, n4, n5] edges = [e0, e1, e2, e3, e4, e5, e6, e7, e8, e9, e10, e11, e12, e13, e14] graph = Graph(nodes, edges, inputs, outputs) small_graph = Graph(inputs=[g], outputs=[h], edges=[e14]) g1, g2 = qf.connected_graphs_split(graph) assert g1 == small_graph or g2 == small_graph
def _wires_to_connection_point_node_shortcut(_wires, _edges, _nodes_group, _is_output, _is_matrix_2, _cp_dict): index = 0 for node in _nodes_group: # we are iterating over the nodes to ensure an order, indeed since the two halves are done depending on the # node order in this V2.0 of the algorithm, we can simply ensure the order by giving the I/O indexes # depending on node position for edge in edges: if node in edge: wire_index = None if set(edge).intersection(wires): # in this case, the edge links a node to an original I/O wire_index = wires.index( list(set(edge).intersection(wires))[0]) if edge.name in [wire.name for wire in wires]: # when a diagram is split, the edges between two diagrams are considered as connected to a new # virtual output, with the name of the edge as wire name wire_index = wires.index(Wire(edge.name)) if wire_index is not None: point = ConnectionPoint(is_matrix_2=_is_matrix_2, is_out=is_output, index=index) _cp_dict[wire_index] = point index += 1 return _cp_dict
def test_wires_to_connection_point_edge_sorted(): b0 = Wire("b0") b1 = Wire("b1") b2 = Wire("b2") b3 = Wire("b3") e2 = Edge("e2", b3, b1) e1 = Edge("e1", b0, b2) wires = [b0, b1] edges1 = [e2] edges2 = [e1] result = qf.wires_to_connection_point_edge_sorted(wires, edges1, edges2, False) c0 = ConnectionPoint(is_matrix_2=True, is_out=False) c1 = ConnectionPoint(index=1, is_matrix_2=False, is_out=False) expected_result = [c0, c1] assert result == expected_result
def test_matrix_linker(): v0 = Node("v0", arity=2, node_type="hadamard") v3 = Node("v3", arity=2, node_type="hadamard") v5 = Node("v5", angle=1.0, arity=3, node_type="X") we5 = Wire("e5") we6 = Wire("e6") e5 = Edge("e5", v5, v0) e6 = Edge("e6", v0, v3) graph1 = Graph([v0], [e5, e6], [we5], [we6]) we8 = Wire("e8") v8 = Node("v8", 0.5, arity=4) e8 = Edge("e8", v8, v3) graph2 = Graph([v3], [e8, e6], [we8, we6], []) result = qf.matrix_linker(graph1, graph2) p1 = ConnectionPoint(False, True) p2 = ConnectionPoint(True, False, 1) expected_result = [InterMatrixLink(p1, p2)] assert result == expected_result
def dictionary_to_data(wire_vertices_dictionary: dict, node_vertices_dictionary: dict, undir_edges_dictionary: dict) -> (List[Wire], List[Node], List[Edge]): """dictionary_to_data(wire_vertices_dictionary: dict, node_vertices_dictionary: dict, undir_edges_dictionary: dict) -> List[Wire], List[Node], List[Edge] Converts the three dictionaries (wires, nodes and edges) to lists of classes defined in :ref:`data` Args: wire_vertices_dictionary (dict): dictionary containing the data about the wires node_vertices_dictionary (dict): dictionary containing the data about the nodes undir_edges_dictionary (dict): dictionary containing the data about the edges Returns: List[Wire], List[Node], List[Edge]: lists of wires, nodes and edges """ wires = [] # type: List[Wire] for wire_name in wire_vertices_dictionary: wires.append(Wire(wire_name)) nodes = [] # type: List[Node] for node_name in node_vertices_dictionary: angle = 0. node_type = 'Z' if 'data' in node_vertices_dictionary[node_name]: node_type = node_vertices_dictionary[node_name]['data']['type'] angle = node_vertices_dictionary[node_name]['data']['value'] if angle == '': angle = 0. else: angle = angle.replace('\\pi', '1') angle = angle.replace('pi', '1') angle = angle.replace('Pi', '1') angle = float(eval(angle)) nodes.append(Node(node_name, angle, node_type)) edges = [] # type: List[Edge] for edge_name in undir_edges_dictionary: label = "" if 'data' in undir_edges_dictionary[edge_name]: label = undir_edges_dictionary[edge_name]['data']['label'] n1 = None n2 = None for node in nodes: if node.name == undir_edges_dictionary[edge_name]['src']: node.arity += 1 n1 = node if node.name == undir_edges_dictionary[edge_name]['tgt']: node.arity += 1 n2 = node for wire in wires: if wire.name == undir_edges_dictionary[edge_name]['src']: n1 = wire if wire.name == undir_edges_dictionary[edge_name]['tgt']: n2 = wire edges.append(Edge(edge_name, n1, n2, label)) return wires, nodes, edges
def test_wires_to_connection_point_node_sorted(): we5 = Wire("e5") we8 = Wire("e8") v5 = Node("v5") v8 = Node("v8") v0 = Node("v0", node_type='hadamard', arity=2) v3 = Node("v3", node_type='hadamard', arity=2) e5 = Edge("e5", v5, v0) e6 = Edge("e6", v0, v3) e8 = Edge("e8", v8, v3) wires = [we5, we8] edges = [e8, e6, e5] nodes1 = [v0] nodes2 = [v3] result = qf.wires_to_connection_point_node_sorted(wires, edges, nodes1, nodes2, False) c0 = ConnectionPoint(is_matrix_2=False, is_out=False) c1 = ConnectionPoint(is_matrix_2=True, is_out=False) expected_result = [c0, c1] assert result == expected_result
def test_no_node_matrix(): # # b4 b0 b5 # \ | / # --|- # | # b2 # b0 = Wire("b0") b2 = Wire("b2") b4 = Wire("b4") b5 = Wire("b5") e0 = Edge("e0", b4, b5) e1 = Edge("e1", b0, b2) edges = [e0, e1] inputs = [b4, b0, b5] outputs = [b2] result = qf.no_node_matrix(edges, inputs, outputs) expected_result = Pi4Matrix([[1, 0, 0, 0, 0, 1, 0, 0], [0, 0, 1, 0, 0, 0, 0, 1]]) assert not (result - expected_result).any()
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 test_graph_subtraction(): # 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 = \ 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) 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] graph = Graph(nodes, edges, inputs, outputs) inputs_graph = Graph(inputs=inputs) assert graph - inputs_graph == Graph(nodes, edges, [], outputs)
def test_filter_inputs_outputs_by_edges(): b0 = Wire("b0") b1 = Wire("b1") b2 = Wire("b2") b3 = Wire("b3") b4 = Wire("b4") b5 = Wire("b5") e1 = Edge("e1", b0, b2) edges = [e1] inputs = [b4, b0, b5, b1] outputs = [b3, b2] result = qf.filter_inputs_outputs_by_edges(edges, inputs, outputs) expected_result = [b0], [b2] assert result == expected_result
def matrix_linker(graph1: Graph, graph2: Graph) -> List[InterMatrixLink]: """Creates a list of *InterMatrixLink* from the **m1** to **m2**. Args: graph1 (Graph): graph corresponding to **m1** graph2 (Graph): graph corresponding to **m2** Returns: List[InterMatrixLink]: links between **m1** and **m2** as a *InterMatrixLink* list """ inter_matrix_links = [] for edge in set(graph1.edges).intersection(graph2.edges): wire = Wire(edge.name) is_out = wire in graph1.outputs is_in = wire in graph1.inputs if is_out: index = graph1.outputs.index(wire) elif is_in: index = graph1.inputs.index(wire) else: continue connection_point1 = ConnectionPoint(is_matrix_2=False, is_out=is_out, index=index) is_out = wire in graph2.outputs is_in = wire in graph2.inputs if is_out: index = graph2.outputs.index(wire) elif is_in: index = graph2.inputs.index(wire) else: continue connection_point2 = ConnectionPoint(is_matrix_2=True, is_out=is_out, index=index) link = InterMatrixLink(connection_point1, connection_point2) inter_matrix_links.append(link) return inter_matrix_links
def test_no_node_edges_detection_true(): assert qf.no_node_edges_detection([Edge("e0", Wire("b0"), Wire("b1"))])
def test_no_node_edges_detection_false(): assert not qf.no_node_edges_detection([Edge("e0", Node("n0"), Wire("b1"))])
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)