示例#1
0
def vibronic_to_general(model):
    new_model = defaultdict(list)
    for e_k, e_v in model.items():
        for kk, vv in e_v.items():
            # it's difficult to rename `kk` because sometimes it's related to
            # phonons sometimes it's `"J"`
            if e_k == "I":
                # operators without electronic dof, simple phonon
                new_model[kk] = vv
            else:
                # operators with electronic dof
                assert isinstance(e_k, tuple) and len(e_k) == 2
                if e_k[0] == e_k[1]:
                    # diagonal
                    new_e_k = (e_k[0], )
                    e_op = (Op(r"a^\dagger a", 0), )
                else:
                    # off-diagonal
                    new_e_k = e_k
                    e_op = (Op(r"a^\dagger", 1), Op("a", -1))
                if kk == "J":
                    new_model[new_e_k] = [e_op + (vv, )]
                else:
                    for term in vv:
                        new_key = new_e_k + kk
                        new_value = e_op + term
                        new_model[new_key].append(new_value)
    return new_model
示例#2
0
    def _construct_flux_operator(self):
        # construct flux operator
        logger.info("constructing 1-d Holstein model flux operator ")

        if isinstance(self.mol_list, MolList):
            if self.mol_list.periodic:
                itera = range(len(self.mol_list))
            else:
                itera = range(len(self.mol_list) - 1)
            j_list = []
            for i in itera:
                conne = (i + 1) % len(self.mol_list)  # connect site index
                j1 = Mpo.intersite(self.mol_list, {
                    i: r"a",
                    conne: r"a^\dagger"
                }, {}, Quantity(self.mol_list.j_matrix[i, conne]))
                j1.compress_config.threshold = 1e-8
                j2 = j1.conj_trans().scale(-1)
                j_list.extend([j1, j2])
            j_oper = compressed_sum(j_list, batchsize=10)

        elif isinstance(self.mol_list, MolList2):

            e_nsite = self.mol_list.n_edofs
            model = {}
            for i in range(e_nsite):
                conne = (i + 1) % e_nsite  # connect site index
                model[(f"e_{i}", f"e_{conne}")] = [
                    (Op(r"a^\dagger",
                        1), Op("a", -1), -self.mol_list.j_matrix[i, conne]),
                    (Op(r"a", -1), Op(r"a^\dagger",
                                      1), self.mol_list.j_matrix[conne, i])
                ]
            j_oper = Mpo.general_mpo(
                self.mol_list,
                model=model,
                model_translator=ModelTranslator.general_model)
        else:
            assert False
        logger.debug(f"flux operator bond dim: {j_oper.bond_dims}")

        return j_oper
示例#3
0
def x_square_average(mol_list: MolList2):
    """
    <x^2> of vibrational DoF
    """
    assert isinstance(mol_list, MolList2)

    mpos = []
    for v_dof in mol_list.v_dofs:
        model = {(v_dof,):[(Op("x^2",0),1.0)]}
        mpo = Mpo.general_mpo(mol_list, model=model,
                model_translator=ModelTranslator.general_model)
        mpos.append(mpo)

    return {r"x^2": mpos}
示例#4
0
    def max_entangled_ex(cls, mol_list, normalize=True):
        """
        T = \\infty locally maximal entangled EX state
        """
        mps = Mps.gs(mol_list, max_entangled=True)
        # the creation operator \\sum_i a^\\dagger_i
        if isinstance(mol_list, MolList):
            ex_mpo = Mpo.onsite(mol_list, r"a^\dagger")
        else:
            model = {}
            for dof in mol_list.e_dofs:
                model[(dof, )] = [(Op("a^\dagger", 1), 1.0)]
            ex_mpo = Mpo.general_mpo(
                mol_list,
                model=model,
                model_translator=ModelTranslator.general_model)

        ex_mps = ex_mpo @ mps
        if normalize:
            ex_mps.normalize(1.0)
        return cls.from_mps(ex_mps)
示例#5
0
def qc_model(h1e, h2e):
    #------------------------------------------------------------------------
    # Jordan-Wigner transformation maps fermion problem into spin problem
    #
    # |0> => |alpha> and |1> => |beta >:
    #
    #    a_j^+ => Prod_{l=1}^{j-1}(sigma_z[l]) * sigma_-[j]
    #    a_j   => Prod_{l=1}^{j-1}(sigma_z[l]) * sigma_+[j]
    #------------------------------------------------------------------------

    norbs = h1e.shape[0]
    logger.info(f"spin norbs: {norbs}")
    assert np.all(np.array(h1e.shape) == norbs)
    assert np.all(np.array(h2e.shape) == norbs)

    model = defaultdict(list)

    # sigma_-, sigma_+, sigma_z
    # one electron
    for idxs in itertools.product(range(norbs), repeat=2):
        sort = np.argsort(idxs)

        key = tuple()
        op = tuple()
        line1 = []
        line2 = []
        for idx in range(idxs[sort[0]], idxs[sort[1]] + 1):
            key += (f"e_{idx}", )
            if idx == idxs[0]:
                line1.append(("sigma_-", 1))
            elif idx < idxs[0]:
                line1.append(("sigma_z", 0))
            else:
                line1.append(("", 0))

            if idx == idxs[1]:
                line2.append(("sigma_+", -1))
            elif idx < idxs[1]:
                line2.append(("sigma_z", 0))
            else:
                line2.append(("", 0))

        npermute = 0
        for term1, term2 in zip(line1, line2):
            ops = list(filter(lambda a: a != "", [term1[0], term2[0]]))

            sz_idx = [i for i, j in enumerate(ops) if j == "sigma_z"]
            for index, i in enumerate(sz_idx):
                npermute += i - len(sz_idx[:index])
            ops = list(filter(lambda a: a != "sigma_z", ops))

            ops = " ".join(ops)
            if len(sz_idx) % 2 == 1:
                ops = ("sigma_z " + ops).strip()

            if ops == "":
                ops = "I"

            op += (Op(ops, term1[1] + term2[1]), )

        #print(idxs)
        #print(key)
        #print(op)
        op += (h1e[idxs[0], idxs[1]] * (-1)**(npermute % 2), )
        model[key].append(op)

    #2e term
    for q, s in itertools.product(range(norbs), repeat=2):
        # a^\dagger_p a^\dagger_q a_r a_s
        for p, r in itertools.product(range(q), range(s)):
            idxs = [p, q, r, s]
            sort = np.argsort(idxs)

            key = tuple()
            op = tuple()
            line1 = []
            line2 = []
            line3 = []
            line4 = []

            for idx in range(idxs[sort[0]], idxs[sort[3]] + 1):
                key += (f"e_{idx}", )
                if idx == p:
                    line1.append(("sigma_-", 1))
                elif idx < p:
                    line1.append(("sigma_z", 0))
                else:
                    line1.append(("", 0))

                if idx == q:
                    line2.append(("sigma_-", 1))
                elif idx < q:
                    line2.append(("sigma_z", 0))
                else:
                    line2.append(("", 0))

                if idx == r:
                    line3.append(("sigma_+", -1))
                elif idx < r:
                    line3.append(("sigma_z", 0))
                else:
                    line3.append(("", 0))

                if idx == s:
                    line4.append(("sigma_+", -1))
                elif idx < s:
                    line4.append(("sigma_z", 0))
                else:
                    line4.append(("", 0))

            npermute = 0
            for term1, term2, term3, term4 in zip(line1, line2, line3, line4):
                ops = [
                    op for op in [term1[0], term2[0], term3[0], term4[0]]
                    if op != ""
                ]
                sz_idx = [i for i, j in enumerate(ops) if j == "sigma_z"]
                #for index, i in enumerate(sz_idx):
                #    npermute += i - len(sz_idx[:index])
                nn = len(sz_idx)
                npermute += sum(sz_idx) - int((nn - 1) * nn / 2)
                ops = [op for op in ops if op != "sigma_z"]

                ops = " ".join(ops)
                if nn % 2 == 1:
                    ops = ("sigma_z " + ops).strip()

                if ops == "":
                    ops = "I"
                op += (Op(ops, term1[1] + term2[1] + term3[1] + term4[1]), )

            #print(p,q,r,s)
            #print(key)
            #print(op,(-1)**(npermute%2), npermute)
            op += (h2e[p, q, r, s] * (-1)**(npermute % 2), )
            model[key].append(op)

    return model
示例#6
0
    def MolList_to_MolList2(cls, mol_list, formula="vibronic"):
        """
        switch from MolList to MolList2
        """

        order = {}
        basis = []
        model = {}
        mapping = {}

        if mol_list.scheme < 4:
            idx = 0
            nv = 0
            for imol, mol in enumerate(mol_list):
                order[f"e_{imol}"] = idx
                if np.allclose(mol.tunnel, 0):
                    basis.append(ba.BasisSimpleElectron())
                else:
                    basis.append(ba.BasisHalfSpin())
                idx += 1
                for iph, ph in enumerate(mol.dmrg_phs):
                    order[f"v_{nv}"] = idx
                    mapping[(imol, iph)] = f"v_{nv}"
                    basis.append(ba.BasisSHO(ph.omega[0], ph.n_phys_dim))
                    idx += 1
                    nv += 1

        elif mol_list.scheme == 4:

            n_left_mol = mol_list.mol_num // 2

            idx = 0
            n_left_ph = 0
            nv = 0
            for imol, mol in enumerate(mol_list):
                for iph, ph in enumerate(mol.dmrg_phs):
                    if imol < n_left_mol:
                        order[f"v_{nv}"] = idx
                        n_left_ph += 1
                    else:
                        order[f"v_{nv}"] = idx + 1

                    basis.append(ba.BasisSHO(ph.omega[0], ph.n_phys_dim))
                    mapping[(imol, iph)] = f"v_{nv}"

                    nv += 1
                    idx += 1

            for imol in range(mol_list.mol_num):
                order[f"e_{imol}"] = n_left_ph
            basis.insert(n_left_ph, ba.BasisMultiElectronVac(mol_list.mol_num))

        else:
            raise ValueError(f"invalid mol_list.scheme: {mol_list.scheme}")

        # model
        if formula == "vibronic":
            # electronic term
            for imol in range(mol_list.mol_num):
                for jmol in range(mol_list.mol_num):
                    if imol == jmol:
                        model[(f"e_{imol}", f"e_{jmol}")] = {
                            "J":
                            mol_list[imol].elocalex + mol_list[imol].dmrg_e0
                        }
                    else:
                        model[(f"e_{imol}", f"e_{jmol}")] = {
                            "J": mol_list.j_matrix[imol, jmol]
                        }

            # vibration part
            model["I"] = {}
            for imol, mol in enumerate(mol_list):
                for iph, ph in enumerate(mol.dmrg_phs):
                    assert np.allclose(np.array(ph.force3rd), [0.0, 0.0])

                    model["I"][(mapping[(imol, iph)], )] = [
                        (Op("p^2", 0), 0.5),
                        (Op("x^2", 0), 0.5 * ph.omega[0]**2)
                    ]

            # vibration potential part
            for imol, mol in enumerate(mol_list):
                for iph, ph in enumerate(mol.dmrg_phs):
                    if np.allclose(ph.omega[0], ph.omega[1]):
                        model[(f"e_{imol}", f"e_{imol}")][(mapping[(imol,iph)],)] \
                            = [(Op("x", 0), -ph.omega[1]**2*ph.dis[1])]

                    else:
                        model[(f"e_{imol}", f"e_{imol}")][(mapping[(imol,iph)],)] \
                            = [
                                (Op("x^2", 0), 0.5*(ph.omega[1]**2-ph.omega[0]**2)),
                                (Op("x", 0), -ph.omega[1]**2*ph.dis[1]),
                                ]

            model_translator = ModelTranslator.vibronic_model

        elif formula == "general":
            # electronic term
            for imol in range(mol_list.mol_num):
                for jmol in range(mol_list.mol_num):
                    if imol == jmol:
                        model[(f"e_{imol}",)] = \
                        [(Op(r"a^\dagger a", 0),
                            mol_list[imol].elocalex + mol_list[imol].dmrg_e0)]
                    else:
                        model[(f"e_{imol}", f"e_{jmol}")] = \
                            [(Op(r"a^\dagger", 1), Op("a", -1),
                                mol_list.j_matrix[imol, jmol])]
            # vibration part
            for imol, mol in enumerate(mol_list):
                for iph, ph in enumerate(mol.dmrg_phs):
                    assert np.allclose(np.array(ph.force3rd), [0.0, 0.0])

                    model[(mapping[(imol, iph)], )] = [(Op("p^2", 0), 0.5),
                                                       (Op("x^2", 0),
                                                        0.5 * ph.omega[0]**2)]

            # vibration potential part
            for imol, mol in enumerate(mol_list):
                for iph, ph in enumerate(mol.dmrg_phs):
                    if np.allclose(ph.omega[0], ph.omega[1]):
                        model[(f"e_{imol}", f"{mapping[(imol,iph)]}")] = [
                            (Op(r"a^\dagger a",
                                0), Op("x", 0), -ph.omega[1]**2 * ph.dis[1]),
                        ]
                    else:
                        model[(f"e_{imol}", f"{mapping[(imol,iph)]}")] = [
                            (Op(r"a^\dagger a", 0), Op("x^2", 0),
                             0.5 * (ph.omega[1]**2 - ph.omega[0]**2)),
                            (Op(r"a^\dagger a",
                                0), Op("x", 0), -ph.omega[1]**2 * ph.dis[1]),
                        ]

            model_translator = ModelTranslator.general_model
        else:
            raise ValueError(f"invalid formula: {formula}")

        dipole = {}
        for imol, mol in enumerate(mol_list):
            dipole[(f"e_{imol}", )] = mol.dipole

        mol_list2 = cls(order, basis, model, model_translator, dipole=dipole)
        mol_list2.map = mapping

        return mol_list2
示例#7
0
def test_general_mpo_others():

    mol_list2 = MolList2.MolList_to_MolList2(mol_list)

    # onsite
    mpo_std = Mpo.onsite(mol_list, r"a^\dagger", mol_idx_set=[0])
    mpo = Mpo.onsite(mol_list2, r"a^\dagger", mol_idx_set=[0])
    check_result(mpo, mpo_std)
    # general method
    mpo = Mpo.general_mpo(mol_list2,
                          model={("e_0", ): [(Op(r"a^\dagger", 0), 1.0)]},
                          model_translator=ModelTranslator.general_model)
    check_result(mpo, mpo_std)

    mpo_std = Mpo.onsite(mol_list, r"a^\dagger a", dipole=True)
    mpo = Mpo.onsite(mol_list2, r"a^\dagger a", dipole=True)
    check_result(mpo, mpo_std)
    mpo = Mpo.general_mpo(mol_list2,
                          model={
                              ("e_0", ):
                              [(Op(r"a^\dagger a",
                                   0), mol_list2.dipole[("e_0", )])],
                              ("e_1", ):
                              [(Op(r"a^\dagger a",
                                   0), mol_list2.dipole[("e_1", )])],
                              ("e_2", ): [(Op(r"a^\dagger a",
                                              0), mol_list2.dipole[("e_2", )])]
                          },
                          model_translator=ModelTranslator.general_model)
    check_result(mpo, mpo_std)

    # intersite
    mpo_std = Mpo.intersite(mol_list, {
        0: r"a^\dagger",
        2: "a"
    }, {(0, 1): "b^\dagger"}, Quantity(2.0))
    mpo = Mpo.intersite(mol_list2, {
        0: r"a^\dagger",
        2: "a"
    }, {(0, 1): r"b^\dagger"}, Quantity(2.0))
    check_result(mpo, mpo_std)
    mpo = Mpo.general_mpo(mol_list2,
                          model={
                              ("e_0", "e_2", "v_1"):
                              [(Op(r"a^\dagger",
                                   1), Op(r"a", -1), Op(r"b^\dagger", 0), 2.0)]
                          },
                          model_translator=ModelTranslator.general_model)
    check_result(mpo, mpo_std)

    # phsite
    mpo_std = Mpo.ph_onsite(mol_list, r"b^\dagger", 0, 0)
    mpo = Mpo.ph_onsite(mol_list2, r"b^\dagger", 0, 0)
    check_result(mpo, mpo_std)
    mpo = Mpo.general_mpo(mol_list2,
                          model={
                              (mol_list2.map[(0, 0)], ): [(Op(r"b^\dagger",
                                                              0), 1.0)]
                          },
                          model_translator=ModelTranslator.general_model)
    check_result(mpo, mpo_std)
示例#8
0
def construct_vibronic_model(multi_e):
    r"""
    Bi-linear vibronic coupling model for Pyrazine, 4 modes
    See: Raab, Worth, Meyer, Cederbaum.  J.Chem.Phys. 110 (1999) 936
    The parameters are from heidelberg mctdh package pyr4+.op
    """
    # frequencies
    w10a = 0.1139 * ev2au
    w6a = 0.0739 * ev2au
    w1 = 0.1258 * ev2au
    w9a = 0.1525 * ev2au

    # energy-gap
    delta = 0.42300 * ev2au

    # linear, on-diagonal coupling coefficients
    # H(1,1)
    _6a_s1_s1 = 0.09806 * ev2au
    _1_s1_s1 = 0.05033 * ev2au
    _9a_s1_s1 = 0.14521 * ev2au
    # H(2,2)
    _6a_s2_s2 = -0.13545 * ev2au
    _1_s2_s2 = 0.17100 * ev2au
    _9a_s2_s2 = 0.03746 * ev2au

    # quadratic, on-diagonal coupling coefficients
    # H(1,1)
    _10a_10a_s1_s1 = -0.01159 * ev2au
    _6a_6a_s1_s1 = 0.00000 * ev2au
    _1_1_s1_s1 = 0.00000 * ev2au
    _9a_9a_s1_s1 = 0.00000 * ev2au
    # H(2,2)
    _10a_10a_s2_s2 = -0.01159 * ev2au
    _6a_6a_s2_s2 = 0.00000 * ev2au
    _1_1_s2_s2 = 0.00000 * ev2au
    _9a_9a_s2_s2 = 0.00000 * ev2au

    # bilinear, on-diagonal coupling coefficients
    # H(1,1)
    _6a_1_s1_s1 = 0.00108 * ev2au
    _1_9a_s1_s1 = -0.00474 * ev2au
    _6a_9a_s1_s1 = 0.00204 * ev2au
    # H(2,2)
    _6a_1_s2_s2 = -0.00298 * ev2au
    _1_9a_s2_s2 = -0.00155 * ev2au
    _6a_9a_s2_s2 = 0.00189 * ev2au

    # linear, off-diagonal coupling coefficients
    _10a_s1_s2 = 0.20804 * ev2au

    # bilinear, off-diagonal coupling coefficients
    # H(1,2) and H(2,1)
    _1_10a_s1_s2 = 0.00553 * ev2au
    _6a_10a_s1_s2 = 0.01000 * ev2au
    _9a_10a_s1_s2 = 0.00126 * ev2au

    model = {}
    e_list = ["s1", "s2"]
    v_list = ["10a", "6a", "9a", "1"]
    for e_idx, e_isymbol in enumerate(e_list):
        for e_jdx, e_jsymbol in enumerate(e_list):
            e_idx_tuple = (f"e_{e_idx}", f"e_{e_jdx}")
            model[e_idx_tuple] = defaultdict(list)
            for v_idx, v_isymbol in enumerate(v_list):
                for v_jdx, v_jsymbol in enumerate(v_list):
                    if v_idx == v_jdx:
                        v_idx_tuple = (f"v_{v_idx}", )
                    else:
                        v_idx_tuple = (f"v_{v_idx}", f"v_{v_jdx}")

                    # linear
                    if v_idx == v_jdx:
                        # if one of the permutation is defined, then the `e_idx_tuple` term should
                        # be defined as required by Hermitian Hamiltonian
                        for eterm1, eterm2 in permut(
                            [f"{e_isymbol}", f"{e_jsymbol}"], 2):
                            factor = locals().get(
                                f"_{v_isymbol}_{eterm1}_{eterm2}")
                            if factor is not None:
                                factor *= np.sqrt(eval(f"w{v_isymbol}"))
                                model[e_idx_tuple][v_idx_tuple].append(
                                    (Op("x", 0), factor))
                                logger.debug(
                                    f"term: {v_isymbol}_{e_isymbol}_{e_jsymbol}"
                                )
                                break
                            else:
                                logger.debug(
                                    f"no term: {v_isymbol}_{e_isymbol}_{e_jsymbol}"
                                )

                    # quadratic
                    # use product to guarantee `break` breaks the whole loop
                    it = product(permut([f"{v_isymbol}", f"{v_jsymbol}"], 2),
                                 permut([f"{e_isymbol}", f"{e_jsymbol}"], 2))
                    for (vterm1, vterm2), (eterm1, eterm2) in it:
                        logger.info(f"_{vterm1}_{vterm2}_{eterm1}_{eterm2}")
                        factor = locals().get(
                            f"_{vterm1}_{vterm2}_{eterm1}_{eterm2}")

                        if factor is not None:
                            factor *= np.sqrt(
                                eval(f"w{v_isymbol}") * eval(f"w{v_jsymbol}"))
                            if v_idx == v_jdx:
                                model_term = (Op("x^2", 0), factor)
                            else:
                                model_term = (Op("x", 0), Op("x", 0), factor)
                            model[e_idx_tuple][v_idx_tuple].append(model_term)
                            logger.debug(
                                f"term: {v_isymbol}_{v_jsymbol}_{e_isymbol}_{e_jsymbol}"
                            )
                            break
                        else:
                            logger.debug(
                                f"no term: {v_isymbol}_{v_jsymbol}_{e_isymbol}_{e_jsymbol}"
                            )

    # electronic coupling
    model[("e_0", "e_0")]["J"] = -delta
    model[("e_1", "e_1")]["J"] = delta

    # vibrational kinetic and potential
    model["I"] = {}
    for v_idx, v_isymbol in enumerate(v_list):
        model["I"][(f"v_{v_idx}", )] = [(Op("p^2", 0), 0.5),
                                        (Op("x^2", 0),
                                         0.5 * eval("w" + v_isymbol)**2)]

    for e_dof, value in model.items():
        for v_dof, ops in value.items():
            if v_dof == "J":
                logger.info(f"{e_dof},{v_dof},{ops}")
            else:
                for term in ops:
                    logger.info(f"{e_dof},{v_dof},{term}")

    order = {}
    basis = []
    if not multi_e:
        idx = 0
        for e_idx, e_isymbol in enumerate(e_list):
            order[f"e_{e_idx}"] = idx
            basis.append(ba.BasisSimpleElectron())
            idx += 1
    else:
        order = {"e_0": 0, "e_1": 0}
        basis.append(ba.BasisMultiElectron(2, [0, 0]))
        idx = 1

    for v_idx, v_isymbol in enumerate(v_list):
        order[f"v_{v_idx}"] = idx
        basis.append(ba.BasisSHO(locals()[f"w{v_isymbol}"], 30))
        idx += 1

    logger.info(f"order:{order}")
    logger.info(f"basis:{basis}")

    return order, basis, model