def test_split_node_names(backend): a = tn.Node(np.zeros((2, 3, 4, 5, 6)), backend=backend) left_edges = [] for i in range(3): left_edges.append(a[i]) right_edges = [] for i in range(3, 5): right_edges.append(a[i]) left, right, _ = tn.split_node(a, left_edges, right_edges, left_name='left', right_name='right', edge_name='edge') assert left.name == 'left' assert right.name == 'right' assert left.edges[-1].name == 'edge' assert right.edges[0].name == 'edge'
def test_reachable_disconnected_1(backend): nodes = [tn.Node(np.random.rand(2, 2, 2), backend=backend) for _ in range(4)] nodes[0][1] ^ nodes[1][0] nodes[2][1] ^ nodes[3][0] assert set(tn.reachable([nodes[0], nodes[2]])) == set(nodes) assert set(tn.reachable([nodes[0]])) == {nodes[0], nodes[1]} assert set(tn.reachable([nodes[1]])) == {nodes[0], nodes[1]} assert set(tn.reachable([nodes[0], nodes[1]])) == {nodes[0], nodes[1]} assert set(tn.reachable([nodes[2]])) == {nodes[2], nodes[3]} assert set(tn.reachable([nodes[3]])) == {nodes[2], nodes[3]} assert set(tn.reachable([nodes[2], nodes[3]])) == {nodes[2], nodes[3]} assert set(tn.reachable([nodes[0], nodes[1], nodes[2]])) == set(nodes) assert set(tn.reachable([nodes[0], nodes[1], nodes[3]])) == set(nodes) assert set(tn.reachable([nodes[0], nodes[2], nodes[3]])) == set(nodes) assert set(tn.reachable([nodes[1], nodes[2], nodes[3]])) == set(nodes)
def test_split_node_full_svd_names(backend): a = tn.Node(np.random.rand(10, 10), backend=backend) e1 = a[0] e2 = a[1] left, s, right, _, = tn.split_node_full_svd( a, [e1], [e2], left_name='left', middle_name='center', right_name='right', left_edge_name='left_edge', right_edge_name='right_edge') assert left.name == 'left' assert s.name == 'center' assert right.name == 'right' assert left.edges[-1].name == 'left_edge' assert s[0].name == 'left_edge' assert s[1].name == 'right_edge' assert right.edges[0].name == 'right_edge'
def test_split_node_qr_unitarity_complex(backend): if backend == "pytorch": pytest.skip("Complex numbers currently not supported in PyTorch") if backend == "jax": pytest.skip("Complex QR crashes jax") a = tn.Node(np.random.rand(3, 3) + 1j * np.random.rand(3, 3), backend=backend) q, r = tn.split_node_qr(a, [a[0]], [a[1]]) q[1] | r[0] qbar = tn.conj(q) q[1] ^ qbar[1] u1 = q @ qbar qbar[0] ^ q[0] u2 = qbar @ q np.testing.assert_almost_equal(u1.tensor, np.eye(3)) np.testing.assert_almost_equal(u2.tensor, np.eye(3))
def test_split_node_rq(dtype, num_charges): np.random.seed(10) a = tn.Node(get_random((6, 7, 8, 9, 10), num_charges, dtype=dtype), backend='symmetric') left_edges = [] for i in range(3): left_edges.append(a[i]) right_edges = [] for i in range(3, 5): right_edges.append(a[i]) left, right = tn.split_node_rq(a, left_edges, right_edges) tn.check_correct([left, right]) result = tn.contract(left[3]) np.testing.assert_allclose(result.tensor.data, a.tensor.data) assert np.all([ charge_equal(result.tensor._charges[n], a.tensor._charges[n]) for n in range(len(a.tensor._charges)) ])
def f(input_vec, blocks, rank, dim, bond_dim, label_len): mps, edges = create_MPS_labeled(blocks, rank, dim, bond_dim) data_tensor = [] for p in tf.unstack(input_vec): data_tensor.append(tn.Node([1 - p, p])) edges.append(data_tensor[0][0] ^ mps[0][0]) half_len = np.int(rank / 2) [ edges.append(data_tensor[i][0] ^ mps[i][1]) for i in range(1, half_len) ] [edges.append(data_tensor[i-label_len][0] ^ mps[i][1]) \ for i in range(half_len + label_len, rank + label_len)] for k in reversed(range(len(edges))): A = tn.contract(edges[k]) #result = tf.math.log(A.tensor) result = A.tensor - tf.math.reduce_max(A.tensor) return result
def _build_right_envs(self, rpos): R = tn.Node(np.array([[[1]]]), backend=self.backend, name='right_env_{}'.format(self._len)) renvs = [R] for i in reversed(range(rpos, self._len)): self.psi.position(i - 1) nodes = [ R, self.psi.nodes[i], self.H.nodes[i], tn.conj(self.psi.nodes[i]) ] R = tn.ncon(nodes, [(1, 3, 5), (-1, 2, 1), (-2, 2, 4, 3), (-3, 4, 5)], backend=self.backend) R.set_name('right_env_{}'.format(i)) renvs.append(R) return renvs
def test_split_node_full_svd_names(num_charges): np.random.seed(10) a = tn.Node(get_random((10, 10), num_charges=num_charges), backend='symmetric') e1 = a[0] e2 = a[1] left, s, right, _, = tn.split_node_full_svd(a, [e1], [e2], left_name='left', middle_name='center', right_name='right', left_edge_name='left_edge', right_edge_name='right_edge') assert left.name == 'left' assert s.name == 'center' assert right.name == 'right' assert left.edges[-1].name == 'left_edge' assert s[0].name == 'left_edge' assert s[1].name == 'right_edge' assert right.edges[0].name == 'right_edge'
def renyiEntropy(n, w, h, M, randOption, theta, phi, estimateFunc, arguments, filename, d=2, excludeIndices=[]): start = datetime.now() avg = 0 N = w * h for m in range(M * 2**N * 10): ops = [ getNonUnitaryRandomOps(d, randOption, theta, phi, vecsNum=n) for i in range(N) ] for ind in excludeIndices: ops[ind] = [tn.Node(np.eye(d, dtype=complex)) for i in range(n)] estimation = 1 for i in range(n): expectation = wrapper(estimateFunc, arguments + [[op[i] for op in ops]]) estimation *= expectation if estimation > 40: b = 1 avg += estimation if m % M == M - 1: with open( filename + '_n_' + str(n) + '_w_' + str(w) + '_h_' + str(h) + '_' + randOption + '_M_' + str(M) + '_m_' + str(m), 'wb') as f: pickle.dump(avg, f) avg = 0 for op in ops: bops.removeState(op) end = datetime.now() with open(filename + '_time_N_' + str(N) + '_M_' + str(M), 'wb') as f: pickle.dump((end - start).total_seconds(), f)
def test_split_node_qr_names(num_charges): np.random.seed(10) a = tn.Node(get_random((2, 3, 4, 5, 6), num_charges=num_charges), backend='symmetric') left_edges = [] for i in range(3): left_edges.append(a[i]) right_edges = [] for i in range(3, 5): right_edges.append(a[i]) left, right = tn.split_node_qr(a, left_edges, right_edges, left_name='left', right_name='right', edge_name='edge') assert left.name == 'left' assert right.name == 'right' assert left.edges[-1].name == 'edge' assert right.edges[0].name == 'edge'
def test_copy_method_with_trace_edges(backend): a = tn.Node(np.ones([3, 3, 3, 3, 3]), name='mynode', axis_names=['a', 'b', 'c', 'd', 'e'], backend=backend) a.add_edge(tn.Edge(a, 0, name='named_edge1'), 0) a.add_edge(tn.Edge(a, 1, name='named_edge2'), 1) a.add_edge(tn.Edge(a, 2, name='named_edge3'), 2) a.add_edge(tn.Edge(a, 3, name='named_edge4'), 3) a.add_edge(tn.Edge(a, 4, name='named_edge5'), 4) a[0] ^ a[3] a[1] ^ a[4] b = a.copy() assert a.name == b.name assert a.shape == b.shape assert a.axis_names == b.axis_names for i in range(len(a.edges)): assert a[i].name == b[i].name assert b[0] is b[3] assert b[1] is b[4] np.testing.assert_allclose(a.tensor, b.tensor)
def test_constructor(backend): psi_tensor = np.random.rand(2, 2) psi_node = tn.Node(psi_tensor, backend=backend) op = qu.quantum_constructor([psi_node[0]], [psi_node[1]]) assert not op.is_scalar() assert not op.is_vector() assert not op.is_adjoint_vector() assert len(op.out_edges) == 1 assert len(op.in_edges) == 1 assert op.out_edges[0] is psi_node[0] assert op.in_edges[0] is psi_node[1] op = qu.quantum_constructor([psi_node[0], psi_node[1]], []) assert not op.is_scalar() assert op.is_vector() assert not op.is_adjoint_vector() assert len(op.out_edges) == 2 assert len(op.in_edges) == 0 assert op.out_edges[0] is psi_node[0] assert op.out_edges[1] is psi_node[1] op = qu.quantum_constructor([], [psi_node[0], psi_node[1]]) assert not op.is_scalar() assert not op.is_vector() assert op.is_adjoint_vector() assert len(op.out_edges) == 0 assert len(op.in_edges) == 2 assert op.in_edges[0] is psi_node[0] assert op.in_edges[1] is psi_node[1] with pytest.raises(ValueError): op = qu.quantum_constructor([], [], [psi_node]) _ = psi_node[0] ^ psi_node[1] op = qu.quantum_constructor([], [], [psi_node]) assert op.is_scalar() assert not op.is_vector() assert not op.is_adjoint_vector() assert len(op.out_edges) == 0 assert len(op.in_edges) == 0
def __gradient__(self, input_idx: int) -> np.ndarray: """ Calculates the gradient of the inner-product <MPS|input_tensor> with respect to the bond tensor. :param input_tensor: tensor formed from the input vector :return: d<MPS|input_tensor>/d{bond} """ proj = self.__projection__(input_idx) bond = self.mps.get_contracted_bond() leftmost = self.__leftmost__() if leftmost: proj[0]['in'] ^ bond['in1'] proj[1]['in'] ^ bond['in2'] proj[2]['in'] ^ bond['r'] else: proj[0]['in'] ^ bond['l'] proj[1]['in'] ^ bond['in1'] proj[2]['in'] ^ bond['in2'] proj[3]['in'] ^ bond['r'] mps_prod = tn.contractors.auto(proj + [bond]) - tn.Node(self.Ys[:, input_idx]) mps_prod.add_axis_names(['out']) proj2 = tn.replicate_nodes(proj) if leftmost: in1_edge = proj2[0]['in'] in2_edge = proj2[1]['in'] r_edge = proj2[2]['in'] edge_order = [r_edge, in1_edge, in2_edge] else: l_edge = proj2[0]['in'] in1_edge = proj2[1]['in'] in2_edge = proj2[2]['in'] r_edge = proj2[3]['in'] edge_order = [l_edge, r_edge, in1_edge, in2_edge] edge_order.append(mps_prod['out']) return tn.contractors.auto([mps_prod] + proj2, output_edge_order=edge_order).tensor
def localVecsEstimate(psi: List[tn.Node], vs: List[List[np.array]], half='left'): vs = np.round(vs, 10) n = len(vs) result = 1 for copy in range(n): if half == 'left': NA = len(vs[0]) curr = bops.multiContraction(psi[NA], psi[NA], '12', '12*') sites = range(NA - 1, -1, -1) elif half == 'right': NA = len(psi) - len(vs[0]) curr = bops.multiContraction(psi[len(psi) - NA - 1], psi[len(psi) - NA - 1], '01', '01*') sites = range(NA, len(psi)) psiCopy = bops.copyState(psi) for alpha in sites: toEstimate = np.outer(vs[copy][alpha - NA], np.conj(vs[np.mod(copy + 1, n)][alpha - NA])) psiCopy[alpha] = bops.permute(bops.multiContraction(psiCopy[alpha], tn.Node(toEstimate), \ '1', '1'), [0, 2, 1]) if half == 'left': curr = bops.multiContraction(bops.multiContraction( psiCopy[alpha], curr, '2', '0', cleanOr2=True), psi[alpha], '12', '12*', cleanOr1=True) elif half == 'right': curr = bops.multiContraction(bops.multiContraction( curr, psiCopy[alpha], '0', '0', cleanOr2=True), psi[alpha], '01', '01*', cleanOr1=True) # psiCopy = bops.shiftWorkingSite(psiCopy, alpha, '<<') result *= np.trace(curr.tensor) tn.remove_node(curr) bops.removeState(psiCopy) return result
def wire_network(tensor_list, give_dense=False): """ Convert list of tensor cores into fully wired network of TN Nodes If give_dense=True, the wired network is contracted together and a single (large) tensor is returned """ num_cores = len(tensor_list) assert valid_formatting(tensor_list) # Wire together all internal edges connecting cores node_list = [tn.Node(core) for core in tensor_list] for i in range(num_cores): for j in range(i + 1, num_cores): node_list[i][j] ^ node_list[j][i] if give_dense: edge_order = [node[i] for i, node in enumerate(node_list)] return contract_network(node_list, edge_order=edge_order) else: return node_list
def _add_node(self, A, wires, name="UnnamedNode"): """Adds a node to the underlying tensor network. The node is also added to ``self._nodes`` for bookkeeping. Args: A (array): numerical data values for the operator (i.e., matrix form) wires (list[int]): wires that this operator acts on name (str): optional name for the node Returns: tn.Node: the newly created node """ name = "{}{}".format(name, tuple(w for w in wires)) if isinstance(A, tn.Node): A.set_name(name) node = A else: node = tn.Node(A, name=name) self._nodes.append(node) return node
def apply_consecutive_gates(self, i, gate): """ Applies a two-qubit gate to the i'th and the (i+1)'th qubit. The gate must be either a 4x4 matrix or a 2x2x2x2 tensor. Comment: In tn.split_node, it may make sense to use max_truncation_err option instead of max_singular_values option, in case there was a mistake in my note. Args: i(int): Index of the qubit. gate(np.array): 4x4 matrix or 2x2x2x2 tensor. """ gate = normalize_gate(gate) j = (i + 1) % self.n_qubits # get dimension of inner bond between i and i+1 r = self.right_edges(i)[0].dimension gate = tn.Node(gate) # connect i and i+1 to the gate self.out_edge(i) ^ gate[0] self.out_edge(j) ^ gate[1] # Get non-dangling edges for each side left_edges = self.left_edges(i) + [gate[2]] right_edges = self.right_edges(j) + [gate[3]] # Contract edges C = (self.nodes[i] @ self.nodes[j]) D = C @ gate self.nodes[i], self.nodes[j], _ = tn.split_node( D, left_edges=left_edges, right_edges=right_edges, max_singular_values=4 * r, left_name=str(i), right_name=str(j))
def computational_basis_state(state: int, dim: int = 2) -> tn.Node: """Returns a computational basis state. Args: state: Integer which labels the state from the set {0, 1, ..., d - 1}. dim: Dimension of the qudit. Default is two for qubits. Raises: ValueError: If state < 0, dim < 0, or state >= dim. """ if state < 0: raise ValueError(f"Argument state should be positive but is {state}.") if dim < 0: raise ValueError(f"Argument dim should be positive but is {dim}.") if state >= dim: raise ValueError( f"Requires state < dim but state = {state} and dim = {dim}.") vector = np.zeros((dim, )) vector[state] = 1. return tn.Node(vector, name=f"|{state}>")
def put_mps(tensors): ''' returns a set of tensor cores connected in MPS a set of connected edges ''' mps = [] for i in range(len(tensors)): mps.append( tn.Node(tensors[i].detach().clone().squeeze(), backend=backend)) if len(tensors) == 1: return mps, [] connected_edges = [] conn = mps[0][-1] ^ mps[1][0] if len(mps) > 2: for k in range(1, len(tensors) - 1): conn = mps[k][-1] ^ mps[k + 1][0] connected_edges.append(conn) return mps, connected_edges
def test_split_node(dtype, num_charges): np.random.seed(111) a = tn.Node(get_zeros((2, 3, 4, 5, 6), num_charges, dtype), backend='symmetric') left_edges = [] for i in range(3): left_edges.append(a[i]) right_edges = [] for i in range(3, 5): right_edges.append(a[i]) left, right, _ = tn.split_node(a, left_edges, right_edges) tn.check_correct({left, right}) actual = left @ right np.testing.assert_allclose(actual.tensor.shape, (2, 3, 4, 5, 6)) np.testing.assert_allclose(a.tensor.shape, (2, 3, 4, 5, 6)) np.testing.assert_allclose(left.tensor.data, 0) np.testing.assert_allclose(right.tensor.data, 0) assert np.all([ charge_equal(a.tensor._charges[n], actual.tensor._charges[n]) for n in range(len(a.tensor._charges)) ])
def getPurity(l): with open('results/toricBoundaries', 'rb') as f: [upRow, downRow, leftRow, rightRow, openA, openB] = pickle.load(f) upRow = tn.Node(upRow) downRow = tn.Node(downRow) leftRow = tn.Node(leftRow) rightRow = tn.Node(rightRow) openA = tn.Node(openA) openB = tn.Node(openB) [cUp, dUp, te] = bops.svdTruncation(upRow, [0, 1], [2, 3], '>>') [cDown, dDown, te] = bops.svdTruncation(downRow, [0, 1], [2, 3], '>>') norm = applyLocalOperators(cUp, dUp, cDown, dDown, leftRow, rightRow, A, B, l, [tn.Node(np.eye(d)) for i in range(l * 4)]) leftRow = bops.multNode(leftRow, 1 / norm) res = applyLocalOperators(cUp, dUp, cDown, dDown, leftRow, rightRow, A, B, l, [tn.Node(ru.proj0Tensor) for i in range(l * 4)]) # The density matrix is constructed of blocks of ones of size N and normalized by res. # Squaring it adds a factor of N * res. N = 2**l purity = N * res return purity
def apply_circuit(circ, qubits=None, init_state=None): if 'tn' not in globals(): raise ImportError('Please install tensornetwork first.') all_nodes, left_edges, right_edges, N = circuit_network(circ) if qubits is not None: assert len(qubits) == N else: qubits = list(range(N)) if init_state is None: init_state_nodes = [ tn.Node(np.array([1, 0], dtype=complex)) for i in range(N) ] init_state_edges = [init_state_nodes[i][0] for i in range(N)] else: init_state_nodes, init_state_edges = init_state all_nodes.extend(init_state_nodes) qubit_edges = [left_edges[q] for q in qubits] for i, q in enumerate(qubits): tn.connect(right_edges[i], init_state_edges[q]) result = tn.contractors.optimal(all_nodes, output_edge_order=qubit_edges) return result.tensor.reshape(-1)
def rgate(seed: Optional[int] = None, angle_scale: float = 1.0): """Returns the random single qubit gate described in https://arxiv.org/abs/2002.07730. Args: seed: Seed for random number generator. angle_scale: Floating point value to scale angles by. Default 1. """ if seed: np.random.seed(seed) # Get the random parameters theta, alpha, phi = np.random.rand(3) * 2 * np.pi mx = np.sin(alpha) * np.cos(phi) my = np.sin(alpha) * np.sin(phi) mz = np.cos(alpha) theta *= angle_scale # Get the unitary unitary = expm(-1j * theta * (mx * _xmatrix + my * _ymatrix * mz * _zmatrix)) return tn.Node(unitary)
def computational_basis_projector(state: int, dim: int = 2) -> tn.Node: """Returns a projector onto a computational basis state which acts on a single qudit of dimension dim. Args: state: Basis state to project onto. dim: Dimension of the qudit. Default is two for qubits. Raises: ValueError: If state < 0, dim < 0, or state >= dim. """ if state < 0: raise ValueError(f"Argument state should be positive but is {state}.") if dim < 0: raise ValueError(f"Argument dim should be positive but is {dim}.") if state >= dim: raise ValueError( f"Requires state < dim but state = {state} and dim = {dim}.") projector = np.zeros((dim, dim)) projector[state, state] = 1. return tn.Node(projector, name=f"|{state}><{state}|")
def translate(self, alpha: float, max_singular_values: int, num_threads: int) -> bool: B = self.Xs.shape[1] num_per_thd = int(B / num_threads) bond = self.mps.get_contracted_bond() bond_shape = bond.shape leftmost = self.__leftmost__() if leftmost: grads = np.zeros((num_threads, bond_shape[0], bond_shape[1], bond_shape[2], bond_shape[3])) else: grads = np.zeros((num_threads, bond_shape[0], bond_shape[1], bond_shape[2], bond_shape[3], bond_shape[4])) iter_barrier = threading.Barrier(num_threads + 1) def thread_main(ithread): grad = np.zeros(bond_shape) for i in np.arange(ithread * num_per_thd, (ithread + 1) * num_per_thd): grad = grad + self.__gradient__(i) grads[ithread] = grad iter_barrier.wait() worker_threads = [threading.Thread(target=thread_main, args=(it,)) for it in range(num_threads)] for t in worker_threads: t.start() iter_barrier.wait() for t in worker_threads: t.join() # Here, all gradients have been calculated total_grad = np.sum(grads, axis=0) new_bond = tn.Node(bond.tensor - alpha * total_grad) self.__add_newbond_names__(new_bond) self.mps.update_bond(new_bond, max_singular_values) for i in range(B): self.__precompute_projections__(i) return self.mps.output_idx < len(self.mps.tensors) - 2
def haar_random_unitary(nqudits: int = 2, qudit_dimension: int = 2, name: str = "Haar", seed: Optional[int] = None) -> tn.Node: """Returns a Haar random unitary matrix of dimension 2**nqubits using the algorithm in https://arxiv.org/abs/math-ph/0609050. Args: nqudits: Number of qudits the unitary acts on. qudit_dimension: Dimension of each qudit. name: Name for the gate. seed: Seed for random number generator. Notes: See also http://qutip.org/docs/4.3/apidoc/functions.html#qutip.random_objects which was used as a template for this function. """ # Seed for random number generator rng = np.random.RandomState(seed) # Generate an N x N matrix of complex standard normal random variables units = np.array([1, 1j]) shape = (qudit_dimension**nqudits, qudit_dimension**nqudits) mat = np.sum(rng.randn(*(shape + (2, ))) * units, axis=-1) / np.sqrt(2) # Do the QR decomposition qmat, rmat = np.linalg.qr(mat) # Create a diagonal matrix by rescaling the diagonal elements of rmat diag = np.diag(rmat).copy() diag /= np.abs(diag) # Reshape the tensor tensor = qmat * diag tensor = np.reshape(tensor, newshape=[qudit_dimension] * 2 * nqudits) return tn.Node(tensor, name=name)
def randomMeasurement(psi, startInd, endInd): d = psi[0].tensor.shape[1] res = [-1] * (endInd - startInd) psiCopy = bops.copyState(psi) for k in [len(psiCopy) - 1 - i for i in range(len(psiCopy) - startInd - 1)]: psiCopy = bops.shiftWorkingSite(psiCopy, k, '<<') for i in range(startInd, endInd): rho = bops.multiContraction(psiCopy[i], psiCopy[i], '02', '02*') measurement = np.random.uniform(low=0, high=1) covered = 0 for s in range(len(rho.tensor)): if covered < measurement < covered + rho.tensor[s, s]: res[i - startInd] = s break covered += rho.tensor[s, s] projectorTensor = np.zeros((d, d), dtype=complex) projectorTensor[res[i - startInd], res[i - startInd]] = 1 projector = tn.Node(projectorTensor, backend=None) bops.applySingleSiteOp(psiCopy, projector, i) psiCopy = bops.shiftWorkingSite(psiCopy, i, '>>') psiCopy[i + 1].tensor /= np.sqrt(bops.getOverlap(psiCopy, psiCopy)) tn.remove_node(rho) bops.removeState(psiCopy) return res
def test_real_physics(dtype, num_charges): # Calcuate the expected value in numpy t1 = get_random_symmetric((20, 20, 20, 20), [False, False, False, False], num_charges, dtype=dtype) t2 = get_random_symmetric((20, 20, 20), [True, False, True], num_charges, dtype=dtype) t3 = get_random_symmetric((20, 20, 20), [True, True, False], num_charges, dtype=dtype) t1_dense = t1.todense() t2_dense = t2.todense() t3_dense = t3.todense() adense = tn.Node(t1_dense, name="T", backend='numpy') bdense = tn.Node(t2_dense, name="A", backend='numpy') cdense = tn.Node(t3_dense, name="B", backend='numpy') e1 = tn.connect(adense[2], bdense[0], "edge") e2 = tn.connect(cdense[0], adense[3], "edge2") e3 = tn.connect(bdense[1], cdense[1], "edge3") node_result = tn.contract(e1) node_result = tn.contract(e2) final_result = tn.contract(e3) # Build the network a = tn.Node(t1, name="T", backend='symmetric') b = tn.Node(t2, name="A", backend='symmetric') c = tn.Node(t3, name="B", backend='symmetric') e1 = tn.connect(a[2], b[0], "edge") e2 = tn.connect(c[0], a[3], "edge2") e3 = tn.connect(b[1], c[1], "edge3") tn.check_correct(tn.reachable(a)) node_result = tn.contract(e1) tn.check_correct(tn.reachable(node_result)) node_result = tn.contract(e2) tn.check_correct(tn.reachable(node_result)) val = tn.contract(e3) tn.check_correct(tn.reachable(val)) np.testing.assert_allclose(val.tensor.todense(), final_result.tensor)
def test_split_node_qr_orig_shape(backend): n1 = tn.Node(np.random.rand(3, 4, 5), backend=backend) tn.split_node_qr(n1, [n1[0], n1[2]], [n1[1]]) np.testing.assert_allclose(n1.shape, (3, 4, 5))
def test_switch_backend_raises_error(backend): a = tn.Node(np.random.rand(3, 3, 3)) a.backend = BaseBackend() with pytest.raises(NotImplementedError): tn.switch_backend({a}, backend)