def test_edge_initialize_raises_error_faulty_arguments(double_node_edge): node1 = double_node_edge.node1 node2 = double_node_edge.node2 with pytest.raises(ValueError): Edge(name="edge", node1=node1, node2=node2, axis1=0) with pytest.raises(ValueError): Edge(name="edge", node1=node1, axis1=0, axis2=0)
def test_edge_magic_xor(double_node_edge): node1 = double_node_edge.node1 node2 = double_node_edge.node2 edge1 = Edge(name="edge1", node1=node1, axis1=2) edge2 = Edge(name="edge2", node1=node2, axis1=2) edge = edge1 ^ edge2 assert edge.node1 == node1 assert edge.node2 == node2
def fixture_double_node_edge(backend): net = tensornetwork.TensorNetwork(backend=backend) tensor = net.backend.convert_to_tensor(np.ones((1, 2, 2))) node1 = Node( tensor=tensor, name="test_node1", axis_names=["a", "b", "c"], network=net) node2 = Node( tensor=tensor, name="test_node2", axis_names=["a", "b", "c"], network=net) net.connect(node1["b"], node2["b"]) edge1 = Edge(name="edge", node1=node1, axis1=0) edge12 = Edge(name="edge", node1=node1, axis1=1, node2=node2, axis2=1) return DoubleNodeEdgeTensor(node1, node2, edge1, edge12, tensor)
def copy(nodes: Iterable[BaseNode], conjugate: bool = False) -> Tuple[dict, dict]: """Copy the given nodes and their edges. This will return a dictionary linking original nodes/edges to their copies. Args: nodes: An `Iterable` (Usually a `List` or `Set`) of `Nodes`. conjugate: Boolean. Whether to conjugate all of the nodes (useful for calculating norms and reduced density matrices). Returns: A tuple containing: node_dict: A dictionary mapping the nodes to their copies. edge_dict: A dictionary mapping the edges to their copies. """ #TODO: add support for copying CopyTensor if conjugate: node_dict = { node: Node(node.backend.conj(node.tensor), name=node.name, axis_names=node.axis_names, backend=node.backend.name) for node in nodes } else: node_dict = { node: Node(node.tensor, name=node.name, axis_names=node.axis_names, backend=node.backend.name) for node in nodes } edge_dict = {} for edge in get_all_edges(nodes): node1 = edge.node1 axis1 = edge.node1.get_axis_number(edge.axis1) if not edge.is_dangling(): node2 = edge.node2 axis2 = edge.node2.get_axis_number(edge.axis2) new_edge = Edge(node_dict[node1], axis1, edge.name, node_dict[node2], axis2) new_edge.set_signature(edge.signature) else: new_edge = Edge(node_dict[node1], axis1, edge.name) node_dict[node1].add_edge(new_edge, axis1) if not edge.is_dangling(): node_dict[node2].add_edge(new_edge, axis2) edge_dict[edge] = new_edge return node_dict, edge_dict
def copy(nodes: Iterable[AbstractNode], conjugate: bool = False) -> Tuple[dict, dict]: """Copy the given nodes and their edges. This will return a tuple linking original nodes/edges to their copies. If nodes A and B are connected but only A is passed in to be copied, the edge between them will become a dangling edge. Args: nodes: An Iterable (Usually a `list` or `set`) of `nodes`. conjugate: Boolean. Whether to conjugate all of the nodes (useful for calculating norms and reduced density matrices). Returns: A tuple containing: node_dict: A dictionary mapping the nodes to their copies. edge_dict: A dictionary mapping the edges to their copies. """ node_dict = {} for node in nodes: node_dict[node] = node.copy(conjugate) edge_dict = {} for edge in get_all_edges(nodes): node1 = edge.node1 axis1 = edge.node1.get_axis_number(edge.axis1) # edge dangling or node2 does not need to be copied if edge.is_dangling() or edge.node2 not in node_dict: new_edge = Edge(node_dict[node1], axis1, edge.name) node_dict[node1].add_edge(new_edge, axis1) edge_dict[edge] = new_edge continue node2 = edge.node2 axis2 = edge.node2.get_axis_number(edge.axis2) # copy node2 but not node1 if node1 not in node_dict: new_edge = Edge(node_dict[node2], axis2, edge.name) node_dict[node2].add_edge(new_edge, axis2) edge_dict[edge] = new_edge continue # both nodes should be copied new_edge = Edge(node_dict[node1], axis1, edge.name, node_dict[node2], axis2) if not edge.is_trace(): node_dict[node2].add_edge(new_edge, axis2) node_dict[node1].add_edge(new_edge, axis1) edge_dict[edge] = new_edge return node_dict, edge_dict
def redirect_edge(edge: Edge, new_node: AbstractNode, old_node: AbstractNode) -> None: """ Redirect `edge` from `old_node` to `new_node`. Routine updates `new_node` and `old_node`. `edge` is added to `new_node`, `old_node` gets a new Edge instead of `edge`. Args: edge: An Edge. new_node: The new `Node` object. old_node: The old `Node` object. Returns: None Raises: ValueError: if `edge` does not point to `old_node`. """ if not edge.is_trace(): if edge.is_dangling(): if edge.node1 is not old_node: raise ValueError(f"edge {edge} is not pointing " f"to old_node {old_node}") edge.node1 = new_node axis = edge.axis1 else: if edge.node1 is old_node: edge.node1 = new_node axis = edge.axis1 elif edge.node2 is old_node: edge.node2 = new_node axis = edge.axis2 else: raise ValueError(f"edge {edge} is not pointing " f"to old_node {old_node}") new_node.add_edge(edge, axis, True) new_edge = Edge(old_node, axis) old_node.add_edge(new_edge, axis, True) else: if edge.node1 is not old_node: raise ValueError(f"edge {edge} is not pointing " f"to old_node {old_node}") edge.node1 = new_node edge.node2 = new_node axis1 = edge.axis1 axis2 = edge.axis2 new_node.add_edge(edge, axis1, True) new_node.add_edge(edge, axis2, True) new_edge = Edge(old_node, axis1, None, old_node, axis2) old_node.add_edge(new_edge, axis1, True) old_node.add_edge(new_edge, axis2, True)
def fixture_double_node_edge(backend): tensor = np.ones((1, 2, 2)) node1 = Node(tensor=tensor, name="test_node1", axis_names=["a", "b", "c"], backend=backend) node2 = Node(tensor=tensor, name="test_node2", axis_names=["a", "b", "c"], backend=backend) tn.connect(node1["b"], node2["b"]) edge1 = Edge(name="edge", node1=node1, axis1=0) edge12 = Edge(name="edge", node1=node1, axis1=1, node2=node2, axis2=1) return DoubleNodeEdgeTensor(node1, node2, edge1, edge12, tensor)
def fixture_single_node_edge(backend): tensor = np.ones((1, 2, 2)) node = Node(tensor=tensor, name="test_node", axis_names=["a", "b", "c"], backend=backend) edge = Edge(name="edge", node1=node, axis1=0) return SingleNodeEdgeTensor(node, edge, tensor)
def test_node_add_edge_raises_error_mismatch_rank(single_node_edge): node = single_node_edge.node edge = single_node_edge.edge with pytest.raises(ValueError): node.add_edge(edge, axis=-1) edge = Edge(name="edge", node1=node, axis1=0) with pytest.raises(ValueError): node.add_edge(edge, axis=3)
def fixture_single_node_edge(backend): net = tensornetwork.TensorNetwork(backend=backend) tensor = np.ones((1, 2, 2)) tensor = net.backend.convert_to_tensor(tensor) node = Node( tensor=tensor, name="test_node", axis_names=["a", "b", "c"], network=net) edge = Edge(name="edge", node1=node, axis1=0) return SingleNodeEdgeTensor(node, edge, tensor)
def test_node_reorder_edges_raise_error_wrong_edges(single_node_edge): node = single_node_edge.node e0 = node[0] e1 = node[1] e2 = node[2] edge = Edge(name="edge", node1=node, axis1=0) with pytest.raises(ValueError) as e: node.reorder_edges([e0]) assert "Missing edges that belong to node found:" in str(e.value) with pytest.raises(ValueError) as e: node.reorder_edges([e0, e1, e2, edge]) assert "Additional edges that do not belong to node found:" in str(e.value)
def nodes_from_json( json_str: str) -> Tuple[List[AbstractNode], Dict[str, Tuple[Edge]]]: """ Create a tensor network from a JSON string representation of a tensor network. Args: json_str: A string representing a JSON serialized tensor network. Returns: A list of nodes making up the tensor network. A dictionary of {str -> (edge,)} bindings. All dictionary values are tuples of Edges. """ network_dict = json.loads(json_str) nodes = [] node_ids = {} edge_lookup = {} edge_binding = {} for n in network_dict['nodes']: node = Node.from_serial_dict(n['attributes']) nodes.append(node) node_ids[n['id']] = node for e in network_dict['edges']: e_nodes = [node_ids.get(n_id) for n_id in e['node_ids']] axes = e['attributes']['axes'] edge = Edge(node1=e_nodes[0], axis1=axes[0], node2=e_nodes[1], axis2=axes[1], name=e['attributes']['name']) edge_lookup[e['id']] = edge for node, axis in zip(e_nodes, axes): if node is not None: node.add_edge(edge, axis, override=True) for k, v in network_dict.get('edge_binding', {}).items(): for e_id in v: edge_binding[k] = edge_binding.get(k, ()) + (edge_lookup[e_id], ) return nodes, edge_binding
def copy(nodes: Iterable[BaseNode], conjugate: bool = False) -> Tuple[dict, dict]: """Copy the given nodes and their edges. This will return a dictionary linking original nodes/edges to their copies. If nodes A and B are connected but only A is passed in to be copied, the edge between them will become a dangling edge. Args: nodes: An `Iterable` (Usually a `List` or `Set`) of `Nodes`. conjugate: Boolean. Whether to conjugate all of the nodes (useful for calculating norms and reduced density matrices). Returns: A tuple containing: node_dict: A dictionary mapping the nodes to their copies. edge_dict: A dictionary mapping the edges to their copies. """ #TODO: add support for copying CopyTensor if conjugate: node_dict = { node: Node( node.backend.conj(node.tensor), name=node.name, axis_names=node.axis_names, backend=node.backend) for node in nodes } else: node_dict = { node: Node( node.tensor, name=node.name, axis_names=node.axis_names, backend=node.backend) for node in nodes } edge_dict = {} for edge in get_all_edges(nodes): node1 = edge.node1 axis1 = edge.node1.get_axis_number(edge.axis1) # edge dangling or node2 does not need to be copied if edge.is_dangling() or edge.node2 not in node_dict: new_edge = Edge(node_dict[node1], axis1, edge.name) node_dict[node1].add_edge(new_edge, axis1) edge_dict[edge] = new_edge continue node2 = edge.node2 axis2 = edge.node2.get_axis_number(edge.axis2) # copy node2 but not node1 if node1 not in node_dict: new_edge = Edge(node_dict[node2], axis2, edge.name) node_dict[node2].add_edge(new_edge, axis2) edge_dict[edge] = new_edge continue # both nodes should be copied new_edge = Edge(node_dict[node1], axis1, edge.name, node_dict[node2], axis2) new_edge.set_signature(edge.signature) node_dict[node2].add_edge(new_edge, axis2) node_dict[node1].add_edge(new_edge, axis1) edge_dict[edge] = new_edge return node_dict, edge_dict
def test_edge_is_being_used_false(single_node_edge): node = single_node_edge.node edge2 = Edge(name="edge", node1=node, axis1=0) assert not edge2.is_being_used()
def test_edge_is_trace_true(single_node_edge): node = single_node_edge.node edge = Edge(name="edge", node1=node, axis1=1, node2=node, axis2=2) assert edge.is_trace()
def test_edge_name_throws_type_error(single_node_edge, name): with pytest.raises(TypeError): Edge(node1=single_node_edge.node, axis1=0, name=name)
def test_edge_signature_setter_disabled_throws_error(single_node_edge): edge = Edge(node1=single_node_edge.node, axis1=0) edge.is_disabled = True with pytest.raises(ValueError): edge.signature = "signature"
def test_edge_node1_throws_value_error(single_node_edge): edge = Edge(node1=single_node_edge.node, axis1=0, name="edge") edge._node1 = None err_msg = "node1 for edge 'edge' no longer exists." with pytest.raises(ValueError, match=err_msg): edge.node1
def test_edge_set_name_throws_type_error(single_node_edge, name): edge = Edge(node1=single_node_edge.node, axis1=0) with pytest.raises(TypeError): edge.set_name(name)