def measure_local_operator(self, ops: List[Tensor], sites: Sequence[int]) -> List: """Measure the expectation value of local operators `ops` site `sites`. Args: ops: A list Tensors of rank 2; the local operators to be measured. sites: Sites where `ops` act. Returns: List: measurements :math:`\\langle` `ops[n]`:math:`\\rangle` for n in `sites` Raises: ValueError if `len(ops) != len(sites)` """ if not len(ops) == len(sites): raise ValueError('measure_1site_ops: len(ops) has to be len(sites)!') right_envs = self.right_envs(sites) left_envs = self.left_envs(sites) res = [] for n, site in enumerate(sites): O = Node(ops[n], backend=self.backend) R = Node(right_envs[site], backend=self.backend) L = Node(left_envs[site], backend=self.backend) A = Node(self.tensors[site], backend=self.backend) conj_A = conj(A) O[1] ^ A[1] O[0] ^ conj_A[1] R[0] ^ A[2] R[1] ^ conj_A[2] L[0] ^ A[0] L[1] ^ conj_A[0] result = L @ A @ O @ conj_A @ R res.append(result.tensor) return res
def check_orthonormality(self, which: Text, site: int) -> Tensor: """Check orthonormality of tensor at site `site`. Args: which: * if `'l'` or `'left'`: check left orthogonality * if `'r`' or `'right'`: check right orthogonality site: The site of the tensor. Returns: scalar `Tensor`: The L2 norm of the deviation from identity. Raises: ValueError: If which is different from 'l','left', 'r' or 'right'. """ if which not in ('l', 'left', 'r', 'right'): raise ValueError( "Wrong value `which`={}. " "`which` as to be 'l','left', 'r' or 'right.".format(which)) n1 = Node( self.get_tensor(site), backend=self.backend) #we need to absorb the connector_matrix n2 = conj(n1) if which in ('l', 'left'): n1[0] ^ n2[0] n1[1] ^ n2[1] else: n1[2] ^ n2[2] n1[1] ^ n2[1] result = n1 @ n2 return self.norm( abs(result.tensor - self.backend.eye( N=result.shape[0], M=result.shape[1], dtype=self.dtype)))
def test_conj(backend): if backend == "pytorch": pytest.skip("Complex numbers currently not supported in PyTorch") a = Node(np.random.rand(3, 3) + 1j * np.random.rand(3, 3), backend=backend) abar = linalg.conj(a) np.testing.assert_allclose(abar.tensor, a.backend.conj(a.tensor))
def measure_two_body_correlator(self, op1: Tensor, op2: Tensor, site1: int, sites2: Sequence[int]) -> List: """ Compute the correlator :math:`\\langle` `op1[site1], op2[s]`:math:`\\rangle` between `site1` and all sites `s` in `sites2`. If `s == site1`, `op2[s]` will be applied first. Args: op1: Tensor of rank 2; the local operator at `site1`. op2: Tensor of rank 2; the local operator at `sites2`. site1: The site where `op1` acts sites2: Sites where operator `op2` acts. Returns: List: Correlator :math:`\\langle` `op1[site1], op2[s]`:math:`\\rangle` for `s` :math:`\\in` `sites2`. Raises: ValueError if `site1` is out of range """ N = len(self) if site1 < 0: raise ValueError( "Site site1 out of range: {} not between 0 <= site < N = {}.".format( site1, N)) sites2 = np.array(sites2) #enable logical indexing # we break the computation into two parts: # first we get all correlators <op2(site2) op1(site1)> with site2 < site1 # then all correlators <op1(site1) op2(site2)> with site2 >= site1 # get all sites smaller than site1 left_sites = np.sort(sites2[sites2 < site1]) # get all sites larger than site1 right_sites = np.sort(sites2[sites2 > site1]) # compute all neccessary right reduced # density matrices in one go. This is # more efficient than calling right_envs # for each site individually rs = self.right_envs( np.append(site1, np.mod(right_sites, N)).astype(np.int64)) ls = self.left_envs( np.append(np.mod(left_sites, N), site1).astype(np.int64)) c = [] if len(left_sites) > 0: A = Node(self.tensors[site1], backend=self.backend) O1 = Node(op1, backend=self.backend) conj_A = conj(A) R = Node(rs[site1], backend=self.backend) R[0] ^ A[2] R[1] ^ conj_A[2] A[1] ^ O1[1] conj_A[1] ^ O1[0] R = ((R @ A) @ O1) @ conj_A n1 = np.min(left_sites) # -- A-------- # | | # compute op1(site1) | # | | # -- A*------- # and evolve it to the left by contracting tensors at site2 < site1 # if site2 is in `sites2`, calculate the observable # # ---A--........-- A-------- # | | | | # | op2(site2) op1(site1)| # | | | | # ---A--........-- A*------- for n in range(site1 - 1, n1 - 1, -1): if n in left_sites: A = Node(self.tensors[n % N], backend=self.backend) conj_A = conj(A) O2 = Node(op2, backend=self.backend) L = Node(ls[n % N], backend=self.backend) L[0] ^ A[0] L[1] ^ conj_A[0] O2[0] ^ conj_A[1] O2[1] ^ A[1] R[0] ^ A[2] R[1] ^ conj_A[2] res = (((L @ A) @ O2) @ conj_A) @ R c.append(res.tensor) if n > n1: R = Node( self.apply_transfer_operator(n % N, 'right', R.tensor), backend=self.backend) c = list(reversed(c)) # compute <op2(site1)op1(site1)> if site1 in sites2: O1 = Node(op1, backend=self.backend) O2 = Node(op2, backend=self.backend) L = Node(ls[site1], backend=self.backend) R = Node(rs[site1], backend=self.backend) A = Node(self.tensors[site1], backend=self.backend) conj_A = conj(A) O1[1] ^ O2[0] L[0] ^ A[0] L[1] ^ conj_A[0] R[0] ^ A[2] R[1] ^ conj_A[2] A[1] ^ O2[1] conj_A[1] ^ O1[0] O = O1 @ O2 res = (((L @ A) @ O) @ conj_A) @ R c.append(res.tensor) # compute <op1(site1) op2(site2)> for site1 < site2 if len(right_sites) > 0: A = Node(self.tensors[site1], backend=self.backend) conj_A = conj(A) L = Node(ls[site1], backend=self.backend) O1 = Node(op1, backend=self.backend) L[0] ^ A[0] L[1] ^ conj_A[0] A[1] ^ O1[1] conj_A[1] ^ O1[0] L = L @ A @ O1 @ conj_A n2 = np.max(right_sites) # -- A-- # | | # compute | op1(site1) # | | # -- A*-- # and evolve it to the right by contracting tensors at site2 > site1 # if site2 is in `sites2`, calculate the observable # # ---A--........-- A-------- # | | | | # | op1(site1) op2(site2)| # | | | | # ---A--........-- A*------- for n in range(site1 + 1, n2 + 1): if n in right_sites: R = Node(rs[n % N], backend=self.backend) A = Node(self.tensors[n % N], backend=self.backend) conj_A = conj(A) O2 = Node(op2, backend=self.backend) A[0] ^ L[0] conj_A[0] ^ L[1] O2[0] ^ conj_A[1] O2[1] ^ A[1] R[0] ^ A[2] R[1] ^ conj_A[2] res = L @ A @ O2 @ conj_A @ R c.append(res.tensor) if n < n2: L = Node( self.apply_transfer_operator(n % N, 'left', L.tensor), backend=self.backend) return c
def right_envs(self, sites: Sequence[int]) -> Dict: """Compute right reduced density matrices for site `sites. This returns a dict `right_envs` mapping sites (int) to Tensors. `right_envs[site]` is the right-reduced density matrix to the right of site `site`. Args: sites (list of int): A list of sites of the MPS. Returns: `dict` mapping `int` to `Tensor`: The right-reduced density matrices at each site in `sites`. """ sites = np.array(sites) if len(sites) == 0: return {} if self.center_position is not None: center_position = self.center_position else: center_position = len(self.tensors) - 1 n1 = np.min(sites) #check if all elements of `sites` are within allowed range if not np.all(sites < len(self)): raise ValueError( 'all elements of `sites` have to be < N = {}'.format( len(self))) if not np.all(sites >= -1): raise ValueError('all elements of `sites` have to be >= -1') # right-reduced density matrices to the right of `center_position` # (including center_position) are all identities right_sites = sites[sites >= center_position] right_envs = {} for site in right_sites: right_envs[site] = Node(self.backend.eye( N=self.tensors[site].shape[2], dtype=self.dtype), backend=self.backend) # right reduced density matrices at sites < center_position # have to be calculated from a network contraction if n1 < center_position: nodes = {} conj_nodes = {} for site in reversed(range(n1 + 1, center_position + 1)): nodes[site] = Node(self.tensors[site], backend=self.backend) conj_nodes[site] = conj(nodes[site]) nodes[center_position][2] ^ conj_nodes[center_position][2] nodes[center_position][1] ^ conj_nodes[center_position][1] for site in reversed(range(n1 + 1, center_position)): nodes[site][2] ^ nodes[site + 1][0] conj_nodes[site][2] ^ conj_nodes[site + 1][0] nodes[site][1] ^ conj_nodes[site][1] edges = {site: node[0] for site, node in nodes.items()} conj_edges = {site: node[0] for site, node in conj_nodes.items()} right_env = contract_between(nodes[center_position], conj_nodes[center_position]) if center_position - 1 in sites: right_env.reorder_edges( [edges[center_position], conj_edges[center_position]]) right_envs[center_position - 1] = right_env for site in reversed(range(n1 + 1, center_position)): right_env = contract_between(right_env, nodes[site]) right_env = contract_between(right_env, conj_nodes[site]) if site - 1 in sites: right_env.reorder_edges([edges[site], conj_edges[site]]) right_envs[site - 1] = right_env return {k: v.tensor for k, v in right_envs.items()}
def left_envs(self, sites: Sequence[int]) -> Dict: """Compute left reduced density matrices for site `sites`. This returns a dict `left_envs` mapping sites (int) to Tensors. `left_envs[site]` is the left-reduced density matrix to the left of site `site`. Args: sites (list of int): A list of sites of the MPS. Returns: `dict` mapping `int` to `Tensor`: The left-reduced density matrices at each site in `sites`. """ sites = np.array(sites) #enable logical indexing if len(sites) == 0: return {} if self.center_position is not None: center_position = self.center_position else: center_position = 0 n2 = np.max(sites) #check if all elements of `sites` are within allowed range if not np.all(sites <= len(self)): raise ValueError( 'all elements of `sites` have to be <= N = {}'.format( len(self))) if not np.all(sites >= 0): raise ValueError('all elements of `sites` have to be positive') # left-reduced density matrices to the left of `center_position` # (including center_position) are all identities left_sites = sites[sites <= center_position] left_envs = {} for site in left_sites: left_envs[site] = Node(self.backend.eye( N=self.tensors[site].shape[0], dtype=self.dtype), backend=self.backend) # left reduced density matrices at sites > center_position # have to be calculated from a network contraction if n2 > center_position: nodes = {} conj_nodes = {} for site in range(center_position, n2): nodes[site] = Node(self.tensors[site], backend=self.backend) conj_nodes[site] = conj(nodes[site]) nodes[center_position][0] ^ conj_nodes[center_position][0] nodes[center_position][1] ^ conj_nodes[center_position][1] for site in range(center_position + 1, n2): nodes[site][0] ^ nodes[site - 1][2] conj_nodes[site][0] ^ conj_nodes[site - 1][2] nodes[site][1] ^ conj_nodes[site][1] edges = {site: node[2] for site, node in nodes.items()} conj_edges = {site: node[2] for site, node in conj_nodes.items()} left_env = contract_between(nodes[center_position], conj_nodes[center_position]) left_env.reorder_edges( [edges[center_position], conj_edges[center_position]]) if center_position + 1 in sites: left_envs[center_position + 1] = left_env for site in range(center_position + 1, n2): left_env = contract_between(left_env, nodes[site]) left_env = contract_between(left_env, conj_nodes[site]) if site + 1 in sites: left_env.reorder_edges([edges[site], conj_edges[site]]) left_envs[site + 1] = left_env return {k: v.tensor for k, v in left_envs.items()}
def test_conj_of_node_without_backend_raises_error(): node = np.random.rand(3, 3, 3) with pytest.raises(AttributeError): linalg.conj(node)