Esempio n. 1
0
    def energy_2x1_1x2(self, state, env):
        r"""
        :param state: wavefunction
        :param env: CTM environment
        :type state: IPEPS
        :type env: ENV
        :return: energy per site
        :rtype: float
        
        We assume iPEPS with 1x2 unit cell containing two tensors A, B
        simple PBC tiling::

            A  Au A  Au
            Bu B  Bu B
            A  Au A  Au
            Bu B  Bu B
    
        where unitary "u" operates on every site of "odd" sublattice to realize
        AFM correlations.
        Taking the reduced density matrix :math:`\rho_{2x1}` (:math:`\rho_{1x2}`) 
        of 2x1 (1x2) cluster given by :py:func:`rdm.rdm2x1` (:py:func:`rdm.rdm1x2`) 
        with indexing of sites as follows :math:`s_0,s_1;s'_0,s'_1` for both types
        of density matrices::

            rdm2x1   rdm1x2

            s0--s1   s0
                     |
                     s1

        and without assuming any symmetry on the indices of individual tensors a following
        set of terms has to be evaluated in order to compute energy-per-site::

               0       0       0
            1--A--3 1--A--3 1--A--3
               2       2       2
               0       0       0
            1--B--3 1--B--3 1--B--3
               2       2       2                             A  B
               0       0                                     2  2
            1--A--3 1--A--3                  A--3 1--A,      0  0
               2       2             , terms B--3 1--B, and  B, A,
        """
        energy = 0.
        for coord, site in state.sites.items():
            # for coord in [(0,0),(0,1),(1,0),(1,1)]:
            rdm2x1 = rdm.rdm2x1(coord, state, env)
            rdm1x2 = rdm.rdm1x2(coord, state, env)
            ss = torch.einsum('ijab,ijab', rdm2x1, self.h2_rot)
            energy += ss
            if coord[1] % 2 == 0:
                ss = torch.einsum('ijab,ijab', rdm1x2, self.h2_rot)
            else:
                ss = torch.einsum('ijab,jiba', rdm1x2,
                                  self.alpha * self.h2_rot)
            energy += ss

        # return energy-per-site
        energy_per_site = energy / len(state.sites.items())
        return energy_per_site
Esempio n. 2
0
    def eval_obs(self,state,env):
        obs= dict()
        with torch.no_grad():
            for coord,site in state.sites.items():
                rdm1x1 = rdm.rdm1x1(coord,state,env)
                for label,op in self.obs_ops.items():
                    obs[str(coord)+"|"+label] = torch.trace(rdm1x1@op)


        obs= dict({"avg_m": 0.})
        with torch.no_grad():
            for coord,site in state.sites.items():
                rdm1x1 = rdm.rdm1x1(coord,state,env)
                for label,op in self.obs_ops.items():
                    obs[f"{label}{coord}"]= torch.trace(rdm1x1@op)
                obs[f"m{coord}"]= sqrt(abs(obs[f"sz{coord}"]**2 + obs[f"sp{coord}"]*obs[f"sm{coord}"]))
                obs["avg_m"] += obs[f"m{coord}"]
            obs["avg_m"]= obs["avg_m"]/len(state.sites.keys())

            for coord,site in state.sites.items():
                rdm2x1 = rdm.rdm2x1(coord,state,env)
                rdm1x2 = rdm.rdm1x2(coord,state,env)
                obs[f"SS2x1{coord}"]= torch.einsum('ijab,ijab',rdm2x1,self.SS)
                obs[f"SS1x2{coord}"]= torch.einsum('ijab,ijab',rdm1x2,self.SS)
        
        # prepare list with labels and values
        obs_labels=["avg_m"]+[f"m{coord}" for coord in state.sites.keys()]\
            +[f"{lc[1]}{lc[0]}" for lc in list(itertools.product(state.sites.keys(), self.obs_ops.keys()))]
        obs_labels += [f"SS2x1{coord}" for coord in state.sites.keys()]
        obs_labels += [f"SS1x2{coord}" for coord in state.sites.keys()]
        obs_values=[obs[label] for label in obs_labels]
        return obs_values, obs_labels
Esempio n. 3
0
    def ctmrg_conv_f(state, env, history, ctm_args=cfg.ctm_args):
        with torch.no_grad():
            if not history:
                history = dict({"log": []})
            dist = float('inf')
            list_rdm = []
            for coord, site in state.sites.items():
                rdm2x1 = rdm.rdm2x1(coord, state, env)
                rdm1x2 = rdm.rdm1x2(coord, state, env)
                list_rdm.extend([rdm2x1, rdm1x2])

            # compute observables
            e_curr = model.energy_2x1_1x2(state, env)
            obs_values, obs_labels = model.eval_obs(state, env)
            print(", ".join([f"{len(history['log'])}", f"{e_curr}"] +
                            [f"{v}" for v in obs_values]))

            if len(history["log"]) > 1:
                dist = 0.
                for i in range(len(list_rdm)):
                    dist += torch.dist(list_rdm[i], history["rdm"][i],
                                       p=2).item()
            history["rdm"] = list_rdm
            history["log"].append(dist)
            if dist < ctm_args.ctm_conv_tol:
                log.info({
                    "history_length": len(history['log']),
                    "history": history['log']
                })
                return True, history
        return False, history
Esempio n. 4
0
        def ctmrg_conv_f(state, env, history, ctm_args=cfg.ctm_args):
            with torch.no_grad():
                if not history:
                    history = dict({"log": []})
                dist = float('inf')
                list_rdm = []
                for coord, site in state.sites.items():
                    rdm2x1 = rdm.rdm2x1(coord, state, env)
                    rdm1x2 = rdm.rdm1x2(coord, state, env)
                    list_rdm.extend([rdm2x1, rdm1x2])

                if len(history["log"]) > 1:
                    dist = 0.
                    for i in range(len(list_rdm)):
                        dist += torch.dist(list_rdm[i], history["rdm"][i],
                                           p=2).item()
                history["rdm"] = list_rdm
                history["log"].append(dist)
                if dist < ctm_args.ctm_conv_tol:
                    log.info({
                        "history_length": len(history['log']),
                        "history": history['log']
                    })
                    return True, history
            return False, history
Esempio n. 5
0
    def energy_2x1_1x2(self, state, env):
        r"""
        :param state: wavefunction
        :param env: CTM environment
        :type state: IPEPS
        :type env: ENV
        :return: energy per site
        :rtype: float
        
        We assume iPEPS with 2x2 unit cell containing four tensors A, B, C, and D with
        simple PBC tiling::

            A B A B
            C D C D
            A B A B
            C D C D

        Taking the reduced density matrix :math:`\rho_{2x1}` (:math:`\rho_{1x2}`) 
        of 2x1 (1x2) cluster given by :py:func:`rdm.rdm2x1` (:py:func:`rdm.rdm1x2`) 
        with indexing of sites as follows :math:`s_0,s_1;s'_0,s'_1` for both types
        of density matrices::

            rdm2x1   rdm1x2

            s0--s1   s0
                     |
                     s1

        and without assuming any symmetry on the indices of individual tensors a following
        set of terms has to be evaluated in order to compute energy-per-site::

               0       0       0
            1--A--3 1--B--3 1--A--3
               2       2       2
               0       0       0
            1--C--3 1--D--3 1--C--3
               2       2       2             A--3 1--B,      A  B  C  D
               0       0                     B--3 1--A,      2  2  2  2
            1--A--3 1--B--3                  C--3 1--D,      0  0  0  0
               2       2             , terms D--3 1--C, and  C, D, A, B
        """
        energy = 0.
        for coord, site in state.sites.items():
            rdm2x1 = rdm.rdm2x1(coord, state, env)
            rdm1x2 = rdm.rdm1x2(coord, state, env)
            ss = einsum('ijab,ijab', rdm2x1, self.h2)
            energy += ss
            if coord[1] % 2 == 0:
                ss = einsum('ijab,ijab', rdm1x2, self.h2)
            else:
                ss = einsum('ijab,ijab', rdm1x2, self.alpha * self.h2)
            energy += ss

        # return energy-per-site
        energy_per_site = energy / len(state.sites.items())
        energy_per_site = _cast_to_real(energy_per_site)

        return energy_per_site
Esempio n. 6
0
    def energy_2x1_1x2(self, state, env):
        energy = 0.
        for coord, site in state.sites.items():
            rdm2x1 = rdm.rdm2x1(coord, state, env)
            rdm1x2 = rdm.rdm1x2(coord, state, env)
            energy += torch.einsum('ijab,ijab', rdm2x1, self.h2)
            energy += torch.einsum('ijab,ijab', rdm1x2, self.h2)

        energy_per_site = energy / len(state.sites.items())
        return energy_per_site
Esempio n. 7
0
    def eval_obs(self, state, env):
        r"""
        :param state: wavefunction
        :param env: CTM environment
        :type state: IPEPS
        :type env: ENV
        :return:  expectation values of observables, labels of observables
        :rtype: list[float], list[str]

        Computes the following observables in order

            1. :math:`\langle 2S^z \rangle,\ \langle 2S^x \rangle` for each site in the unit cell

        """
        obs = dict()
        with torch.no_grad():
            for coord, site in state.sites.items():
                rdm1x1 = rdm.rdm1x1(coord, state, env)
                for label, op in self.obs_ops.items():
                    obs[f"{label}{coord}"] = torch.trace(rdm1x1 @ op)
                obs[f"sx{coord}"] = 0.5 * (obs[f"sp{coord}"] +
                                           obs[f"sm{coord}"])

            for coord, site in state.sites.items():
                rdm2x1 = rdm.rdm2x1(coord, state, env)
                rdm1x2 = rdm.rdm1x2(coord, state, env)
                rdm2x2 = rdm.rdm2x2(coord, state, env)
                obs[f"SzSz2x1{coord}"] = torch.einsum('ijab,ijab', rdm2x1,
                                                      self.h2)
                obs[f"SzSz1x2{coord}"] = torch.einsum('ijab,ijab', rdm1x2,
                                                      self.h2)
                obs[f"SzSzSzSz{coord}"] = torch.einsum('ijklabcd,ijklabcd',
                                                       rdm2x2, self.h4)

        # prepare list with labels and values
        obs_labels = [
            f"{lc[1]}{lc[0]}"
            for lc in list(itertools.product(state.sites.keys(), ["sz", "sx"]))
        ]
        obs_labels += [f"SzSz2x1{coord}" for coord in state.sites.keys()]
        obs_labels += [f"SzSz1x2{coord}" for coord in state.sites.keys()]
        obs_labels += [f"SzSzSzSz{coord}" for coord in state.sites.keys()]
        obs_values = [obs[label] for label in obs_labels]
        return obs_values, obs_labels
Esempio n. 8
0
    def eval_obs_1site_BP(self, state, env):
        r"""
        :param state: wavefunction
        :param env: CTM environment
        :type state: IPEPS
        :type env: ENV
        :return:  expectation values of observables, labels of observables
        :rtype: list[float], list[str]
        
        Evaluates observables for single-site ansatz by including the sublattice
        rotation in the physical space. 
        """

        # TODO optimize/unify ?
        # expect "list" of (observable label, value) pairs ?
        obs = dict({"avg_m": 0.})
        with torch.no_grad():
            for coord, site in state.sites.items():
                rdm1x1 = rdm.rdm1x1(coord, state, env)
                for label, op in self.obs_ops.items():
                    obs[f"{label}{coord}"] = torch.trace(rdm1x1 @ op)
                obs[f"m{coord}"] = sqrt(
                    abs(obs[f"sz{coord}"]**2 +
                        obs[f"sp{coord}"] * obs[f"sm{coord}"]))
                obs["avg_m"] += obs[f"m{coord}"]
            obs["avg_m"] = obs["avg_m"] / len(state.sites.keys())

            for coord, site in state.sites.items():
                rdm2x1 = rdm.rdm2x1(coord, state, env)
                rdm1x2 = rdm.rdm1x2(coord, state, env)
                SS2x1 = torch.einsum('ijab,ijab', rdm2x1, self.SS_rot)
                SS1x2 = torch.einsum('ijab,ijab', rdm1x2, self.SS_rot)
                obs[f"SS2x1{coord}"] = _cast_to_real(SS2x1)
                obs[f"SS1x2{coord}"] = _cast_to_real(SS1x2)

        # prepare list with labels and values
        obs_labels=["avg_m"]+[f"m{coord}" for coord in state.sites.keys()]\
            +[f"{lc[1]}{lc[0]}" for lc in list(itertools.product(state.sites.keys(), self.obs_ops.keys()))]
        obs_labels += [f"SS2x1{coord}" for coord in state.sites.keys()]
        obs_labels += [f"SS1x2{coord}" for coord in state.sites.keys()]
        obs_values = [obs[label] for label in obs_labels]
        return obs_values, obs_labels
Esempio n. 9
0
    def eval_obs(self, state, env):
        r"""
        :param state: wavefunction
        :param env: CTM environment
        :type state: IPEPS
        :type env: ENV
        :return:  expectation values of observables, labels of observables
        :rtype: list[float], list[str]

        Computes the following observables in order

            1. average magnetization over the unit cell,
            2. magnetization for each site in the unit cell
            3. :math:`\langle S^z \rangle,\ \langle S^+ \rangle,\ \langle S^- \rangle` 
               for each site in the unit cell
            4. :math:`\mathbf{S}_i.\mathbf{S}_j` for all non-equivalent nearest neighbour
               bonds

        where the on-site magnetization is defined as
        
        .. math::
            
            \begin{align*}
            m &= \sqrt{ \langle S^z \rangle^2+\langle S^x \rangle^2+\langle S^y \rangle^2 }
            =\sqrt{\langle S^z \rangle^2+1/4(\langle S^+ \rangle+\langle S^- 
            \rangle)^2 -1/4(\langle S^+\rangle-\langle S^-\rangle)^2} \\
              &=\sqrt{\langle S^z \rangle^2 + 1/2\langle S^+ \rangle \langle S^- \rangle)}
            \end{align*}

        Usual spin components can be obtained through the following relations
        
        .. math::
            
            \begin{align*}
            S^+ &=S^x+iS^y               & S^x &= 1/2(S^+ + S^-)\\
            S^- &=S^x-iS^y\ \Rightarrow\ & S^y &=-i/2(S^+ - S^-)
            \end{align*}
        """
        obs = dict({"avg_m": 0.})
        with torch.no_grad():
            rot_op = su2.get_rot_op(self.phys_dim,
                                    dtype=self.dtype,
                                    device=self.device)
            for coord, site in state.sites.items():
                rdm1x1 = rdm.rdm1x1(coord, state, env)
                if coord[1] % 2 == 0: rdm1x1 = rot_op @ rdm1x1 @ rot_op.t()

                for label, op in self.obs_ops.items():
                    obs[f"{label}{coord}"] = torch.trace(rdm1x1 @ op)
                obs[f"m{coord}"] = sqrt(
                    abs(obs[f"sz{coord}"]**2 +
                        obs[f"sp{coord}"] * obs[f"sm{coord}"]))
                obs["avg_m"] += obs[f"m{coord}"]
            obs["avg_m"] = obs["avg_m"] / len(state.sites.keys())

            # for coord,site in state.sites.items():
            for coord in [(0, 0), (0, 1), (1, 0), (1, 1)]:
                rdm2x1 = rdm.rdm2x1(coord, state, env)
                rdm1x2 = rdm.rdm1x2(coord, state, env)
                if (coord[1] % 2 == 0) ^ (coord[0] % 2 == 0):
                    SS1x2 = torch.einsum('ijab,ijab', rdm1x2, self.h2_rot)
                else:
                    SS1x2 = torch.einsum('ijab,jiba', rdm1x2, self.h2_rot)
                obs[f"SS1x2{coord}"] = SS1x2.real if SS1x2.is_complex(
                ) else SS1x2

                if (coord[0] % 2 == 0) ^ (coord[0] % 2 == 0):
                    SS2x1 = torch.einsum('ijab,ijab', rdm2x1, self.h2_rot)
                else:
                    SS2x1 = torch.einsum('ijab,jiba', rdm2x1, self.h2_rot)
                obs[f"SS2x1{coord}"] = SS2x1.real if SS2x1.is_complex(
                ) else SS2x1

        # prepare list with labels and values
        obs_labels=["avg_m"]+[f"m{coord}" for coord in state.sites.keys()]\
            +[f"{lc[1]}{lc[0]}" for lc in list(itertools.product(state.sites.keys(), self.obs_ops.keys()))]
        obs_labels += [
            f"SS2x1{coord}" for coord in [(0, 0), (0, 1), (1, 0), (1, 1)]
        ]
        obs_labels += [
            f"SS1x2{coord}" for coord in [(0, 0), (0, 1), (1, 0), (1, 1)]
        ]
        obs_values = [obs[label] for label in obs_labels]
        return obs_values, obs_labels
Esempio n. 10
0
    def eval_obs(self, state, env):
        r"""
        :param state: wavefunction
        :param env: CTM environment
        :type state: IPEPS
        :type env: ENV
        :return:  expectation values of observables, labels of observables
        :rtype: list[float], list[str]

        Computes the following observables in order

            1. average magnetization over the unit cell,
            2. magnetization for each site in the unit cell
            3. :math:`\langle S^z \rangle,\ \langle S^+ \rangle,\ \langle S^- \rangle` 
               for each site in the unit cell

        where the on-site magnetization is defined as
        
        .. math::
            
            \begin{align*}
            m &= \sqrt{ \langle S^z \rangle^2+\langle S^x \rangle^2+\langle S^y \rangle^2 }
            =\sqrt{\langle S^z \rangle^2+1/4(\langle S^+ \rangle+\langle S^- 
            \rangle)^2 -1/4(\langle S^+\rangle-\langle S^-\rangle)^2} \\
              &=\sqrt{\langle S^z \rangle^2 + 1/2\langle S^+ \rangle \langle S^- \rangle)}
            \end{align*}

        Usual spin components can be obtained through the following relations
        
        .. math::
            
            \begin{align*}
            S^+ &=S^x+iS^y               & S^x &= 1/2(S^+ + S^-)\\
            S^- &=S^x-iS^y\ \Rightarrow\ & S^y &=-i/2(S^+ - S^-)
            \end{align*}
        """
        # TODO optimize/unify ?
        # expect "list" of (observable label, value) pairs ?
        obs = dict({"avg_m": 0.})
        with torch.no_grad():
            for coord, site in state.sites.items():
                rdm1x1 = rdm.rdm1x1(coord, state, env)
                for label, op in self.obs_ops.items():
                    obs[f"{label}{coord}"] = torch.trace(rdm1x1 @ op)
                obs[f"m{coord}"] = sqrt(
                    abs(obs[f"sz{coord}"]**2 +
                        obs[f"sp{coord}"] * obs[f"sm{coord}"]))
                obs["avg_m"] += obs[f"m{coord}"]
            obs["avg_m"] = obs["avg_m"] / len(state.sites.keys())

            for coord, site in state.sites.items():
                rdm2x1 = rdm.rdm2x1(coord, state, env)
                rdm1x2 = rdm.rdm1x2(coord, state, env)
                SS2x1 = torch.einsum('ijab,ijab', rdm2x1, self.h2)
                SS1x2 = torch.einsum('ijab,ijab', rdm1x2, self.h2)
                obs[f"SS2x1{coord}"] = _cast_to_real(SS2x1)
                obs[f"SS1x2{coord}"] = _cast_to_real(SS1x2)

        # prepare list with labels and values
        obs_labels=["avg_m"]+[f"m{coord}" for coord in state.sites.keys()]\
            +[f"{lc[1]}{lc[0]}" for lc in list(itertools.product(state.sites.keys(), self.obs_ops.keys()))]
        obs_labels += [f"SS2x1{coord}" for coord in state.sites.keys()]
        obs_labels += [f"SS1x2{coord}" for coord in state.sites.keys()]
        obs_values = [obs[label] for label in obs_labels]
        return obs_values, obs_labels