Esempio n. 1
0
  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
Esempio n. 2
0
  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)))
Esempio n. 3
0
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))
Esempio n. 4
0
  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
Esempio n. 5
0
    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()}
Esempio n. 6
0
    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()}
Esempio n. 7
0
def test_conj_of_node_without_backend_raises_error():
  node = np.random.rand(3, 3, 3)
  with pytest.raises(AttributeError):
    linalg.conj(node)