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
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
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}
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)
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
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
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)
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