def test_get_all_nondangling(backend): a = tn.Node(np.eye(2), backend=backend) b = tn.Node(np.eye(2), backend=backend) edge1 = tn.connect(a[0], b[0]) c = tn.Node(np.eye(2), backend=backend) d = tn.Node(np.eye(2), backend=backend) edge2 = tn.connect(c[0], d[0]) edge3 = tn.connect(a[1], c[1]) assert {edge1, edge2, edge3} == tn.get_all_nondangling({a, b, c, d})
def test_get_shared_edges(backend): a = tn.Node(np.ones((2, 2, 2)), backend=backend) b = tn.Node(np.ones((2, 2, 2)), backend=backend) c = tn.Node(np.ones((2, 2, 2)), backend=backend) e1 = tn.connect(a[0], b[0]) e2 = tn.connect(b[1], c[1]) e3 = tn.connect(a[2], b[2]) assert tn.get_shared_edges(a, b) == {e1, e3} assert tn.get_shared_edges(b, c) == {e2}
def _apply_op_network(site_edges, op, n1, pbc=False): N = len(site_edges) op_sites = len(op.shape) // 2 n_op = tensornetwork.Node(op, backend="tensorflow") for m in range(op_sites): target_site = (n1 + m) % N if pbc else n1 + m tensornetwork.connect(n_op[op_sites + m], site_edges[target_site]) site_edges[target_site] = n_op[m] return site_edges, n_op
def test_check_connected_value_error(backend): a = tn.Node(np.array([2, 2.]), backend=backend) b = tn.Node(np.array([2, 2.]), backend=backend) tn.connect(a[0], b[0]) c = tn.Node(np.array([2, 2.]), backend=backend) d = tn.Node(np.array([2, 2.]), backend=backend) tn.connect(c[0], d[0]) with pytest.raises(ValueError): tn.check_connected({a, b, c, d})
def test_flatten_edges_different_nodes_value_error(backend): a = tn.Node(np.eye(2), backend=backend) b = tn.Node(np.eye(2), backend=backend) c = tn.Node(np.eye(2), backend=backend) e1 = tn.connect(a[0], b[0]) e2 = tn.connect(a[1], c[0]) tn.connect(b[1], c[1]) with pytest.raises(ValueError): tn.flatten_edges([e1, e2])
def test_reachable_2(backend): a = tn.Node(np.zeros((3, 5)), backend=backend) b = tn.Node(np.zeros((3, 4, 5)), backend=backend) e1 = tn.connect(a[0], b[0]) e2 = tn.connect(a[1], b[2]) nodes = [a, b] edges = [e1, e2] assert set(nodes) == tn.reachable(edges[0]) assert set(nodes) == tn.reachable(edges)
def test_mixed_named_axis(backend): a = tn.Node(np.eye(2) * 2.0, axis_names=["alpha", "beta"], backend=backend) b = tn.Node(np.eye(2) * 3.0, backend=backend) e1 = tn.connect(a["alpha"], b[0]) # Axes should still be indexable by numbers even with naming. e2 = tn.connect(a[1], b[1]) tn.contract(e1) result = tn.contract(e2) np.testing.assert_allclose(result.tensor, 12.0)
def test_double_trace(backend): a = tn.Node(np.ones([10, 10, 10, 10]), name="a", backend=backend) edge1 = tn.connect(a[0], a[1], "edge1") edge2 = tn.connect(a[2], a[3], "edge2") tn.check_correct({a}) val = tn.contract(edge1) tn.check_correct({val}) val = tn.contract(edge2) tn.check_correct({val}) np.testing.assert_allclose(val.tensor, 100.0)
def test_contract_between_trace_edges(dtype, num_charges): a_val = get_random_symmetric((50, 50), [False, True], num_charges, dtype=dtype) final_val = np.trace(a_val.todense()) a = tn.Node(a_val, backend='symmetric') tn.connect(a[0], a[1]) b = tn.contract_between(a, a) tn.check_correct({b}) np.testing.assert_allclose(b.tensor.todense(), final_val)
def test_contract_between_trace_output_edge_order(backend): a_val = np.ones((2, 3, 2, 4)) a = tn.Node(a_val, backend=backend) tn.connect(a[0], a[2]) c = tn.contract_between(a, a, output_edge_order=[a[3], a[1]], axis_names=["3", "1"]) assert c.shape == (4, 3) assert c.axis_names == ["3", "1"]
def test_flatten_trace_consistent_tensor(backend): a_val = np.ones((5, 3, 4, 4, 5)) a = tn.Node(a_val, backend=backend) e1 = tn.connect(a[0], a[4]) e2 = tn.connect(a[3], a[2]) tn.flatten_edges([e2, e1]) tn.check_correct({a}) # Check expected values. a_final = np.reshape(np.transpose(a_val, (1, 2, 0, 3, 4)), (3, 20, 20)) np.testing.assert_allclose(a.tensor, a_final)
def test_replicate_nodes(backend): a = tn.Node(np.random.rand(10, 10), backend=backend) b = tn.Node(np.random.rand(10, 10), backend=backend) c = tn.Node(np.random.rand(10, 10), backend=backend) tn.connect(a[1], b[0]) tn.connect(b[1], c[0]) [a_copy, b_copy] = tn.replicate_nodes([a, b]) assert b_copy in tn.reachable([a_copy]) assert not set([a_copy, b_copy]).issubset(tn.reachable([c])) assert len(b_copy.get_all_dangling()) == 1
def test_remove_node_trace_edge(backend): a = tn.Node(np.ones((2, 2, 2)), backend=backend) b = tn.Node(np.ones(2), backend=backend) tn.connect(a[0], b[0]) tn.connect(a[1], a[2]) _, broken_edges = tn.remove_node(a) assert 0 in broken_edges assert 1 not in broken_edges assert 2 not in broken_edges assert broken_edges[0] is b[0]
def _ev_mps(self, obs_nodes, wires): r"""Expectation value of observables on specified wires using a MPS representation. Args: obs_nodes (Sequence[tn.Node]): the observables as TensorNetwork Nodes wires (Sequence[Sequence[int]]): measured subsystems for each observable Returns: complex: expectation value :math:`\expect{A} = \bra{\psi}A\ket{\psi}` """ if any(len(wires_seq) > 2 for wires_seq in wires): raise NotImplementedError( "Multi-wire measurement only supported for nearest-neighbour wire pairs." ) if len(obs_nodes) == 1 and len(wires[0]) == 1: # TODO: can measure multiple local expectation values at once, # but this would require change of `expval` behaviour and # refactor of `execute` logic from parent class expval = self.mps.measure_local_operator(obs_nodes, wires[0])[0] else: conj_nodes = [tn.conj(node) for node in self.mps.nodes] meas_wires = [] # connect measured bra and ket nodes with observables for obs_node, wire_seq in zip(obs_nodes, wires): if len(wire_seq) == 2 and abs(wire_seq[0] - wire_seq[1]) > 1: raise NotImplementedError( "Multi-wire measurement only supported for nearest-neighbour wire pairs." ) offset = len(wire_seq) for idx, wire in enumerate(wire_seq): tn.connect(conj_nodes[wire][1], obs_node[idx]) tn.connect(obs_node[offset + idx], self.mps.nodes[wire][1]) meas_wires.extend(wire_seq) for wire in range(self.num_wires): # connect unmeasured ket nodes with bra nodes if wire not in meas_wires: tn.connect(conj_nodes[wire][1], self.mps.nodes[wire][1]) # connect local nodes of MPS (not connected by default in tn) if wire != self.num_wires - 1: tn.connect(self.mps.nodes[wire][2], self.mps.nodes[wire + 1][0]) tn.connect(conj_nodes[wire][2], conj_nodes[wire + 1][0]) # contract MPS bonds first bra_node = conj_nodes[0] ket_node = self.mps.nodes[0] for wire in range(self.num_wires - 1): bra_node = tn.contract_between(bra_node, conj_nodes[wire + 1]) ket_node = tn.contract_between(ket_node, self.mps.nodes[wire + 1]) # contract observables into ket for obs_node in obs_nodes: ket_node = tn.contract_between(obs_node, ket_node) # contract bra into observables/ket expval_node = tn.contract_between(bra_node, ket_node) # remove dangling singleton edges expval = self._squeeze(expval_node.tensor) return expval
def test_with_tensors(backend): a = tn.Node(np.eye(2) * 2, name="T", backend=backend) b = tn.Node(np.eye(2) * 3, name="A", backend=backend) e1 = tn.connect(a[0], b[0], "edge") e2 = tn.connect(a[1], b[1], "edge2") tn.check_correct({a, b}) result = tn.contract(e1) tn.check_correct(tn.reachable(result)) val = tn.contract(e2) tn.check_correct(tn.reachable(val)) np.testing.assert_allclose(val.tensor, 12.0)
def test_at_operator(dtype, num_charges): a = tn.Node( get_random_symmetric((50, 50), [False, True], num_charges, dtype=dtype), backend='symmetric') b = tn.Node( get_random_symmetric((50, 50), [False, True], num_charges, dtype=dtype), backend='symmetric') tn.connect(a[1], b[0]) c = a @ b assert isinstance(c, tn.Node) np.testing.assert_allclose(c.tensor.todense(), a.tensor.todense() @ b.tensor.todense())
def test_flatten_trace_edges(backend): a = tn.Node(np.zeros((2, 3, 4, 3, 5, 5)), backend=backend) c = tn.Node(np.zeros((2, 4)), backend=backend) e1 = tn.connect(a[1], a[3]) e2 = tn.connect(a[4], a[5]) external_1 = tn.connect(a[0], c[0]) external_2 = tn.connect(c[1], a[2]) new_edge = tn.flatten_edges([e1, e2], "New Edge") tn.check_correct({a, c}) assert a.shape == (2, 4, 15, 15) assert a.edges == [external_1, external_2, new_edge, new_edge] assert new_edge.name == "New Edge"
def test_split_edges_standard_contract_between(backend): a = tn.Node(np.random.randn(6, 3, 5), name="A", backend=backend) b = tn.Node(np.random.randn(2, 4, 6, 3), name="B", backend=backend) e1 = tn.connect(a[0], b[2], "Edge_1_1") # to be split tn.connect(a[1], b[3], "Edge_1_2") # background standard edge node_dict, _ = tn.copy({a, b}) c_prior = node_dict[a] @ node_dict[b] shape = (2, 1, 3) tn.split_edge(e1, shape) tn.check_correct({a, b}) c_post = tn.contract_between(a, b) np.testing.assert_allclose(c_prior.tensor, c_post.tensor)
def test_complicated_edge_reordering(backend): a = tn.Node(np.zeros((2, 3, 4)), backend=backend) b = tn.Node(np.zeros((2, 5)), backend=backend) c = tn.Node(np.zeros((3, )), backend=backend) d = tn.Node(np.zeros((4, 5)), backend=backend) e_ab = tn.connect(a[0], b[0]) e_bd = tn.connect(b[1], d[1]) e_ac = tn.connect(a[1], c[0]) e_ad = tn.connect(a[2], d[0]) result = tn.contract(e_bd) a.reorder_edges([e_ac, e_ab, e_ad]) tn.check_correct(tn.reachable(result)) assert a.shape == (3, 2, 4)
def test_flatten_trace_consistent_tensor(dtype, num_charges): a_val = get_random_symmetric((5, 5, 5, 5, 5), [False, False, True, True, True], num_charges, dtype=dtype) a = tn.Node(a_val, backend='symmetric') e1 = tn.connect(a[0], a[4]) e2 = tn.connect(a[3], a[2]) tn.flatten_edges([e2, e1]) tn.check_correct({a}) # Check expected values. a_final = np.reshape(np.transpose(a_val.todense(), (1, 2, 0, 3, 4)), (5, 25, 25)) np.testing.assert_allclose(a.tensor.todense(), a_final)
def test_flatten_edges_standard(backend): a = tn.Node(np.zeros((2, 3, 5)), name="A", backend=backend) b = tn.Node(np.zeros((2, 3, 4, 5)), name="B", backend=backend) e1 = tn.connect(a[0], b[0], "Edge_1_1") e2 = tn.connect(a[2], b[3], "Edge_2_3") edge_a_1 = a[1] edge_b_1 = b[1] edge_b_2 = b[2] new_edge = tn.flatten_edges([e1, e2], new_edge_name="New Edge") assert a.shape == (3, 10) assert b.shape == (3, 4, 10) assert a.edges == [edge_a_1, new_edge] assert b.edges == [edge_b_1, edge_b_2, new_edge] tn.check_correct({a, b})
def test_node2_contract_trace(dtype, num_charges): a = tn.Node(get_random_symmetric((20, 20, 20), [False, True, False], num_charges, dtype=dtype), backend='symmetric') b = tn.Node(get_random_symmetric((20, 20), [True, False], num_charges, dtype=dtype), backend='symmetric') tn.connect(b[0], a[2]) trace_edge = tn.connect(a[0], a[1]) c = tn.contract(trace_edge) tn.check_correct({c})
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 test_disable_edges_complex(backend): a = tn.Node(np.eye(2), backend=backend) b = tn.Node(np.eye(2), backend=backend) c = tn.Node(np.eye(2), backend=backend) e1 = tn.connect(a[0], b[0]) e2 = tn.connect(b[1], c[0]) tn.contract(e1) tn.contract(e2) # This now raises an exception because we contract disables contracted edges # and raises a ValueError if we try to access the nodes with pytest.raises(ValueError): e1.node1 with pytest.raises(ValueError): e2.node1
def test_contract_name_contracted_node(backend): node = tn.Node(np.eye(2), name="Identity Matrix", backend=backend) assert node.name == "Identity Matrix" edge = tn.connect(node[0], node[1], name="Trace Edge") assert edge.name == "Trace Edge" final_result = tn.contract(edge, name="Trace Of Identity") assert final_result.name == "Trace Of Identity"
def test_node_reorder_edges_raise_error_trace_edge(single_node_edge): node = single_node_edge.node e2 = tn.connect(node[1], node[2]) e3 = node[0] with pytest.raises(ValueError) as e: node.reorder_edges([e2, e3]) assert "Edge reordering does not support trace edges." in str(e.value)
def test_remove_trace_edge_non_trace_raises_value_error(double_node_edge): node1 = double_node_edge.node1 node2 = double_node_edge.node2 edge = tn.connect(node1[0], node2[0]) edge.name = "e" with pytest.raises(ValueError, match="Edge 'e' is not a trace edge."): _remove_trace_edge(edge, node1)
def test_indirect_trace(backend): a = tn.Node(np.ones([10, 10]), name="a", backend=backend) edge = tn.connect(a[0], a[1], "edge") tn.check_correct({a}) val = tn.contract(edge) tn.check_correct({val}) np.testing.assert_allclose(val.tensor, 10.0)
def test_flatten_consistent_tensor(backend): a_val = np.ones((2, 3, 4, 5)) b_val = np.ones((3, 5, 4, 2)) a = tn.Node(a_val, backend=backend) b = tn.Node(b_val, backend=backend) e1 = tn.connect(a[0], b[3]) e2 = tn.connect(b[1], a[3]) e3 = tn.connect(a[1], b[0]) tn.flatten_edges([e3, e1, e2]) tn.check_correct({a, b}) # Check expected values. a_final = np.reshape(np.transpose(a_val, (2, 1, 0, 3)), (4, 30)) b_final = np.reshape(np.transpose(b_val, (2, 0, 3, 1)), (4, 30)) np.testing.assert_allclose(a.tensor, a_final) np.testing.assert_allclose(b.tensor, b_final)
def test_edge_disable_complex(backend): a = tn.Node(np.eye(2), backend=backend) b = tn.Node(np.eye(2), backend=backend) c = tn.Node(np.eye(2), backend=backend) e1 = tn.connect(a[0], b[0]) e2 = tn.connect(b[1], c[0]) tn.contract(e1) tn.contract(e2) # This now raises an exception because we contract disables contracted edges # and raises a ValueError if we try to access the nodes with pytest.raises(ValueError): e1.node1 # This raises an exception since the intermediate node created when doing # `tn.contract(e2)` was garbage collected. with pytest.raises(ValueError): e2.node1