def check_distance(a: Mps, b: Mps): d1 = (a - b).dmrg_norm d2 = a.distance(b) a_array = a.full_wfn().array b_array = b.full_wfn().array d3 = np.linalg.norm(a_array - b_array) assert d1 == pytest.approx(d2) == pytest.approx(d3)
def optimize_mps(mps: Mps, mpo: Mpo): energies = optimize_mps_dmrg(mps, mpo) if not mps.hybrid_tdh: return energies[-1] # from matplotlib import pyplot as plt # plt.plot(energies); plt.show() HAM = [] for mol in mps.mol_list: for ph in mol.hartree_phs: HAM.append(ph.h_indep) optimize_mps_hartree(mps, HAM) for itera in range(mps.optimize_config.niterations): logging.info("Loop: %d" % itera) MPO, HAM, Etot = mps.construct_hybrid_Ham(mpo) MPS_old = mps.copy() optimize_mps_dmrg(mps, MPO) optimize_mps_hartree(mps, HAM) # check convergence dmrg_converge = abs(mps.angle(MPS_old) - 1) < mps.optimize_config.dmrg_thresh hartree_converge = np.all( mps.hartree_wfn_diff(MPS_old) < mps.optimize_config.hartree_thresh) if dmrg_converge and hartree_converge: logger.info("SCF converge!") break return Etot
def test_distance(): model = custom_model(n_phys_dim=(2, 2)) a = Mps.random(model, 1, 10) b = Mps.random(model, 1, 10) check_distance(a, b) h = Mpo(model) for i in range(100): a = a.evolve(h, 10) b = b.evolve(h, 10) check_distance(a, b)
def test_distance(): mol_list = parameter.custom_mol_list(n_phys_dim=(2, 2)) a = Mps.random(mol_list, 1, 10) b = Mps.random(mol_list, 1, 10) check_distance(a, b) h = Mpo(mol_list) for i in range(100): a = a.evolve(h, 10) b = b.evolve(h, 10) check_distance(a, b)
def test_save_load(): mps = Mpo.onsite(parameter.mol_list, "a^\dagger", mol_idx_set={0}).apply(Mps.gs(parameter.mol_list, False)) mpo = Mpo(parameter.mol_list) mps1 = mps.copy() for i in range(2): mps1 = mps1.evolve(mpo, 10) mps2 = mps.evolve(mpo, 10) fname = "test.npz" mps2.dump(fname) mps2 = Mps.load(parameter.mol_list, fname) mps2 = mps2.evolve(mpo, 10) assert np.allclose(mps1.e_occupations, mps2.e_occupations) os.remove(fname)
def test_save_load(): model = holstein_model mps = Mpo.onsite(model, r"a^\dagger", dof_set={0}) @ Mps.ground_state( model, False) mpo = Mpo(model) mps1 = mps.copy() for i in range(2): mps1 = mps1.evolve(mpo, 10) mps2 = mps.evolve(mpo, 10) fname = "test.npz" mps2.dump(fname) mps2 = Mps.load(model, fname) mps2 = mps2.evolve(mpo, 10) assert np.allclose(mps1.e_occupations, mps2.e_occupations) os.remove(fname)
def test_save_load(): mol_list = custom_mol_list(hartrees=[True, False]) mps = Mpo.onsite(mol_list, "a^\dagger", mol_idx_set={0}) @ Mps.gs( mol_list, False) mpo = Mpo(mol_list) mps1 = mps.copy() for i in range(2): mps1 = mps1.evolve(mpo, 10) mps2 = mps.evolve(mpo, 10) fname = "test.npz" mps2.dump(fname) mps2 = Mps.load(mol_list, fname) mps2 = mps2.evolve(mpo, 10) assert np.allclose(mps1.e_occupations, mps2.e_occupations) os.remove(fname)
def find_highest_energy(h_mpo: Mpo, nexciton, Mmax): logger.debug("begin finding highest energy") model = h_mpo.model mps = Mps.random(model, nexciton, Mmax) mps.optimize_config.inverse = -1.0 energies, _ = optimize_mps(mps, h_mpo) return -energies[-1]
def test_svd_compress(comp, mp): if mp == "mpo": mps = Mpo(holstein_model) M = 22 else: mps = Mps.random(holstein_model, 1, 10) if mp == "mpdm": mps = MpDm.from_mps(mps) mps.canonicalise().normalize() M = 36 if comp: mps = mps.to_complex(inplace=True) print(f"{mps}") mpo = Mpo(holstein_model) if comp: mpo = mpo.scale(-1.0j) print(f"{mpo.bond_dims}") std_mps = mpo.apply(mps, canonicalise=True).canonicalise() print(f"std_mps: {std_mps}") mps.compress_config.bond_dim_max_value = M mps.compress_config.criteria = CompressCriteria.fixed svd_mps = mpo.contract(mps) dis = svd_mps.distance(std_mps) / std_mps.dmrg_norm print(f"svd_mps: {svd_mps}, dis: {dis}") assert np.allclose(dis, 0.0, atol=1e-3) assert np.allclose(svd_mps.dmrg_norm, std_mps.dmrg_norm, atol=1e-4)
def init_mps(self): mmax = self.optimize_config.procedure[0][0] i_mps = Mps.random(self.h_mpo.model, self.nexciton, mmax, 1) i_mps.optimize_config = self.optimize_config energy, i_mps = gs.optimize_mps(i_mps, self.h_mpo) if self.spectratype == "emi": operator = "a" else: operator = r"a^\dagger" dipole_mpo = Mpo.onsite(self.model, operator, dipole=True) if self.temperature != 0: beta = self.temperature.to_beta() # print "beta=", beta # thermal_mpo = Mpo.exact_propagator(self.model, -beta / 2.0, space=self.space1, shift=self.shift1) # ket_mps = thermal_mpo.apply(i_mps) # ket_mps.normalize() # no test, don't know work or not i_mpdm = MpDm.from_mps(i_mps) tp = ThermalProp(i_mpdm, self.h_mpo, exact=True, space=self.space1) tp.evolve(None, 1, beta / 2j) ket_mps = tp.latest_mps else: ket_mps = i_mps a_ket_mps = dipole_mpo.apply(ket_mps, canonicalise=True) a_ket_mps.canonical_normalize() if self.temperature != 0: a_bra_mps = ket_mps.copy() else: a_bra_mps = a_ket_mps.copy() return BraKetPair(a_bra_mps, a_ket_mps)
def test_clear(): gs_mps = Mps.gs(mol_list, max_entangled=False) mps = creation_operator.apply(gs_mps) new_mps = mps.copy() new_mps.clear_memory() assert new_mps.total_bytes < mps.total_bytes check_property(new_mps)
def test_expectations(mpos): random = Mps.random(parameter.holstein_model, 1, 20) e1 = random.expectations(mpos) e2 = random.expectations(mpos, opt=False) assert np.allclose(e1, e2)
def init_mps(self): tentative_mpo = Mpo(self.mol_list) if self.temperature == 0: gs_mp = Mps.gs(self.mol_list, max_entangled=False) if self.dissipation != 0: gs_mp = MpDm.from_mps(gs_mp) else: gs_mp = MpDm.max_entangled_gs(self.mol_list) # subtract the energy otherwise might cause numeric error because of large offset * dbeta energy = Quantity(gs_mp.expectation(tentative_mpo)) mpo = Mpo(self.mol_list, offset=energy) tp = ThermalProp(gs_mp, mpo, exact=True, space="GS") tp.evolve(None, len(gs_mp), self.temperature.to_beta() / 2j) gs_mp = tp.latest_mps init_mp = self.create_electron(gs_mp) if self.dissipation != 0: init_mp = MpDmFull.from_mpdm(init_mp) energy = Quantity(init_mp.expectation(tentative_mpo)) self.mpo = Mpo(self.mol_list, offset=energy) logger.info(f"mpo bond dims: {self.mpo.bond_dims}") logger.info(f"mpo physical dims: {self.mpo.pbond_list}") if self.dissipation != 0: self.mpo = SuperLiouville(self.mpo, self.dissipation) init_mp.canonicalise() init_mp.evolve_config = self.evolve_config # init the compress config if not using threshold and not set if self.compress_config.criteria is not CompressCriteria.threshold\ and self.compress_config.max_dims is None: self.compress_config.set_bonddim(length=len(init_mp) + 1) init_mp.compress_config = self.compress_config # init_mp.invalidate_cache() return init_mp
def test_pyr_4mode(multi_e, translator): order, basis, vibronic_model = construct_vibronic_model(multi_e) if translator is ModelTranslator.vibronic_model: model = vibronic_model elif translator is ModelTranslator.general_model: model = vibronic_to_general(vibronic_model) else: assert False mol_list2 = MolList2(order, basis, model, model_translator=translator) mpo = Mpo(mol_list2) logger.info(f"mpo_bond_dims:{mpo.bond_dims}") mps = Mps.hartree_product_state(mol_list2, condition={"e_1": 1}) # for multi-e case the `expand bond dimension` routine is currently not working # because creation operator is not defined yet mps.use_dummy_qn = True mps.build_empty_qn() compress_config = CompressConfig(CompressCriteria.fixed, max_bonddim=10) evolve_config = EvolveConfig(EvolveMethod.tdvp_ps) job = VibronicModelDynamics(mol_list2, mps0=mps, h_mpo=mpo, compress_config=compress_config, evolve_config=evolve_config) time_step_fs = 2 job.evolve(evolve_dt=time_step_fs * fs2au, nsteps=59) from renormalizer.vibronic.tests.mctdh_data import mctdh_data assert np.allclose(mctdh_data[::round(time_step_fs / 0.5)][:, 1:], job.e_occupations_array, atol=5e-2)
def init_mps(self): tentative_mpo = Mpo(self.model) if self.temperature == 0: gs_mp = Mps.ground_state(self.model, max_entangled=False) else: if self._defined_output_path: gs_mp = load_thermal_state(self.model, self._thermal_dump_path) else: gs_mp = None if gs_mp is None: gs_mp = MpDm.max_entangled_gs(self.model) # subtract the energy otherwise might cause numeric error because of large offset * dbeta energy = Quantity(gs_mp.expectation(tentative_mpo)) mpo = Mpo(self.model, offset=energy) tp = ThermalProp(gs_mp, mpo, exact=True, space="GS") tp.evolve(None, max(20, len(gs_mp)), self.temperature.to_beta() / 2j) gs_mp = tp.latest_mps if self._defined_output_path: gs_mp.dump(self._thermal_dump_path) init_mp = self.create_electron(gs_mp) energy = Quantity(init_mp.expectation(tentative_mpo)) self.mpo = Mpo(self.model, offset=energy) logger.info(f"mpo bond dims: {self.mpo.bond_dims}") logger.info(f"mpo physical dims: {self.mpo.pbond_list}") init_mp.evolve_config = self.evolve_config init_mp.compress_config = self.compress_config if self.evolve_config.is_tdvp: init_mp = init_mp.expand_bond_dimension(self.mpo) init_mp.canonicalise() return init_mp
def test_expectations(mpos): random = Mps.random(parameter.mol_list, 1, 20) e1 = random.expectations(mpos) e2 = random.expectations(mpos, opt=False) assert np.allclose(e1, e2)
def load_wfn(self, model): r"""Load tda wavefunction """ mps_l_cano = Mps.load(model, "mps_l_cano.npz") mps_r_cano = Mps.load(model, "mps_r_cano.npz") tangent_u_dict = np.load("tangent_u.npz") tangent_u = [tangent_u_dict[str(i)] if str(i) in tangent_u_dict.keys() else None for i in range(mps_l_cano.site_num)] tda_coeff_list = [] for iroot in range(self.nroots): tda_coeff_dict = np.load(f"tda_coeff_{iroot}.npz") tda_coeff = [tda_coeff_dict[str(i)] if str(i) in tda_coeff_dict.keys() else None for i in range(mps_l_cano.site_num)] tda_coeff_list.append(tda_coeff) self.wfn = [mps_l_cano, mps_r_cano, tangent_u, tda_coeff_list]
def f(mol_list, run_qutip=True): tentative_mpo = Mpo(mol_list) init_mps = (Mpo.onsite(mol_list, r"a^\dagger", mol_idx_set={0}) @ Mps.gs( mol_list, False)).expand_bond_dimension(hint_mpo=tentative_mpo) init_mpdm = MpDm.from_mps(init_mps).expand_bond_dimension( hint_mpo=tentative_mpo) e = init_mps.expectation(tentative_mpo) mpo = Mpo(mol_list, offset=Quantity(e)) if run_qutip: # calculate result in ZT. FT result is exactly the same TIME_LIMIT = 10 QUTIP_STEP = 0.01 N_POINTS = TIME_LIMIT / QUTIP_STEP + 1 qutip_time_series = np.linspace(0, TIME_LIMIT, N_POINTS) init = qutip.Qobj(init_mps.full_wfn(), [qutip_h.dims[0], [1] * len(qutip_h.dims[0])]) # the result is not exact and the error scale is approximately 1e-5 res = qutip.sesolve(qutip_h - e, init, qutip_time_series, e_ops=[c.dag() * c for c in qutip_clist]) qutip_expectations = np.array(res.expect).T return qutip_expectations, QUTIP_STEP, init_mps, init_mpdm, mpo else: return init_mps, init_mpdm, mpo
def test_from_mps(): gs = Mps.random(parameter.holstein_model, 1, 20) gs_mpdm = MpDm.from_mps(gs) assert np.allclose(gs.e_occupations, gs_mpdm.e_occupations) gs = gs.canonicalise() gs_mpdm = gs_mpdm.canonicalise() assert np.allclose(gs.e_occupations, gs_mpdm.e_occupations)
def test_pyr_4mode(multi_e, dvr): basis, ham_terms = construct_vibronic_model(multi_e, dvr) model = Model(basis, ham_terms) mpo = Mpo(model) logger.info(f"mpo_bond_dims:{mpo.bond_dims}") # same form whether multi_e is True or False init_condition = {"s2": 1} if dvr: for dof in model.v_dofs: idx = model.order[dof] init_condition[dof] = basis[idx].dvr_v[0] mps = Mps.hartree_product_state(model, condition=init_condition) compress_config = CompressConfig(CompressCriteria.fixed, max_bonddim=10) evolve_config = EvolveConfig(EvolveMethod.tdvp_ps) job = VibronicModelDynamics(model, mps0=mps, h_mpo=mpo, compress_config=compress_config, evolve_config=evolve_config, expand=True) time_step_fs = 2 job.evolve(evolve_dt=time_step_fs * fs2au, nsteps=60) from renormalizer.vibronic.tests.mctdh_data import mctdh_data assert np.allclose(mctdh_data[::round(time_step_fs / 0.5)][:61, 1:], job.e_occupations_array, atol=2e-2)
def init_b_mps(self): # get the right hand site vector b, Ax=b # b = -eta * dipole * \psi_0 # only support Holstine model 0/1 exciton manifold if self.spectratype == "abs": nexciton = 0 dipoletype = r"a^\dagger" elif self.spectratype == "emi": nexciton = 1 dipoletype = "a" # procedure for ground state calculation if self.procedure_gs is None: self.procedure_gs = \ [[10, 0.4], [20, 0.2], [30, 0.1], [40, 0], [40, 0]] # ground state calculation mps = Mps.random( self.model, nexciton, self.procedure_gs[0][0], percent=1.0) mps.optimize_config = OptimizeConfig(procedure=self.procedure_gs) mps.optimize_config.method = "2site" energies, mps = gs.optimize_mps(mps, self.h_mpo) e0 = min(energies) dipole_mpo = \ Mpo.onsite( self.model, dipoletype, dipole=True ) b_mps = dipole_mpo.apply(mps.scale(-self.eta)) return b_mps, e0
def check_reduced_density_matrix(basis): model = Model(basis, []) mps = Mps.random(model, 1, 20) rdm = mps.calc_edof_rdm().real assert np.allclose(np.diag(rdm), mps.e_occupations) # only test a sample. Should be enough. mpo = Mpo(model, Op(r"a^\dagger a", [0, 3])) assert rdm[-1][0] == pytest.approx(mps.expectation(mpo))
def test_zt_init_state(): ph = Phonon.simple_phonon(Quantity(1), Quantity(1), 10) mol_list = MolList([Mol(Quantity(0), [ph])], Quantity(0), scheme=3) mpo = Mpo(mol_list) mps = Mps.random(mol_list, 1, 10) optimize_mps(mps, mpo) ct = ChargeTransport(mol_list) assert mps.angle(ct.latest_mps) == pytest.approx(1)
def find_lowest_energy(h_mpo: Mpo, nexciton, Mmax, with_hartree=True): logger.debug("begin finding lowest energy") if with_hartree: mol_list = h_mpo.mol_list else: mol_list = h_mpo.mol_list.get_pure_dmrg_mollist() mps = Mps.random(mol_list, nexciton, Mmax) energy = optimize_mps(mps, h_mpo) return energy.min()
def init_cv_mps(self): # random guess of cv_mps with same qn as b_mps assert self.b_mps is not None # initialize guess of cv_mps cv_mps = Mps.random( self.model, self.b_mps.qntot, self.m_max, percent=1.0) logger.info(f"cv_mps random guess qntot: {cv_mps.qntot}") return cv_mps
def test_environ(): mps = Mps.random(holstein_model, 1, 10) mpo = Mpo(holstein_model) mps = mps.evolve(mpo, 10) environ = Environ(mps, mpo) for i in range(len(mps) - 1): l = environ.read("L", i) r = environ.read("R", i + 1) e = complex(tensordot(l, r, axes=((0, 1, 2), (0, 1, 2)))).real assert pytest.approx(e) == mps.expectation(mpo)
def max_entangled_ex(cls, mol_list, normalize=True): """ T = \\infty maximum entangled EX state """ mps = Mps.gs(mol_list, max_entangled=True) # the creation operator \\sum_i a^\\dagger_i ex_mps = Mpo.onsite(mol_list, r"a^\dagger").apply(mps) if normalize: ex_mps.normalize(1.0) return cls.from_mps(ex_mps)
def test_phonon_onsite(): gs = Mps.gs(mol_list, max_entangled=False) assert not gs.ph_occupations.any() b2 = Mpo.ph_onsite(mol_list, r"b^\dagger", 0, 0) p1 = b2.apply(gs).normalize() assert np.allclose(p1.ph_occupations, [1, 0, 0, 0, 0, 0]) p2 = b2.apply(p1).normalize() assert np.allclose(p2.ph_occupations, [2, 0, 0, 0, 0, 0]) b = b2.conj_trans() assert b.distance(Mpo.ph_onsite(mol_list, r"b", 0, 0)) == 0 assert b.apply(p2).normalize().distance(p1) == pytest.approx(0, abs=1e-5)
def test_csvd(): np.random.seed(0) mps1, mpo = construct_mps_mpo_2(mol_list, procedure[0][0], nexciton) mps1.threshold = 1e-6 mps1.optimize_config.procedure = procedure optimize_mps(mps1, mpo) mps1.compress() mps2 = Mps.load(mol_list, os.path.join(cur_dir, "test_svd_qn.npz")) d = pytest.approx(mps1.distance(mps2), abs=1e-4) # the same direction or opposite direction assert d == 0 or d == 2
def test_displacement(): def get_e_occu(idx): res = np.zeros(len(mol_list)) res[idx] = 1 return res gs = Mps.gs(mol_list, max_entangled=False) gs = Mpo.onsite(mol_list, r"a^\dagger", mol_idx_set={0}).apply(gs).compress() assert np.allclose(gs.e_occupations, get_e_occu(0)) gs = Mpo.displacement(mol_list, 0, 2).apply(gs) assert np.allclose(gs.e_occupations, get_e_occu(2)) gs = Mpo.displacement(mol_list, 2, 0).apply(gs) assert np.allclose(gs.e_occupations ,get_e_occu(0))