Beispiel #1
0
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)
Beispiel #2
0
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)
Beispiel #3
0
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)
Beispiel #4
0
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)
Beispiel #5
0
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)
Beispiel #6
0
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)
Beispiel #7
0
    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
Beispiel #8
0
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]
Beispiel #9
0
    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)
Beispiel #10
0
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)
Beispiel #11
0
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))
Beispiel #12
0
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()
Beispiel #13
0
    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
Beispiel #14
0
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)
Beispiel #15
0
def check_reduced_density_matrix(order, basis):
    mol_list = MolList2(order, basis, {}, ModelTranslator.general_model)
    mps = Mps.random(mol_list, 1, 20)
    rdm = mps.calc_reduced_density_matrix().real
    assert np.allclose(np.diag(rdm), mps.e_occupations)
    # only test a sample. Should be enough.
    mpo = Mpo.general_mpo(mol_list,
                          model={
                              (f"e_0", f"e_3"): [(Op(r"a^\dagger",
                                                     1), Op("a", -1), 1.0)]
                          },
                          model_translator=ModelTranslator.general_model)
    assert rdm[-1][0] == pytest.approx(mps.expectation(mpo))
Beispiel #16
0
def test_site_entropy():
    mps = Mps.random(parameter.holstein_model, 1, 20)
    mps.canonicalise().normalize()
    entropy_1site = mps.calc_entropy("1site")
    entropy_2site = mps.calc_entropy("2site")
    entropy_bond = mps.calc_entropy("bond")
    entropy_mutual = mps.calc_entropy("mutual")
    assert np.allclose(entropy_bond[0], entropy_1site[0])
    assert np.allclose(entropy_bond[-1], entropy_1site[mps.site_num - 1])
    assert np.allclose(entropy_bond[1], entropy_2site[(0, 1)])
    assert np.allclose(entropy_bond[-2],
                       entropy_2site[(mps.site_num - 2, mps.site_num - 1)])
    assert np.allclose(
        entropy_mutual[0, 1],
        (entropy_1site[0] + entropy_1site[1] - entropy_2site[(0, 1)]) / 2)
Beispiel #17
0
def test_environ_multi_mpo(mpdm):
    mps = Mps.random(holstein_model, 1, 10)
    if mpdm:
        mps = MpDm.from_mps(mps)
    mpo = Mpo(holstein_model)
    mps = mps.evolve(mpo, 10)
    environ = Environ(mps, mpo)
    environ_multi_mpo = Environ(mps, [mpo])
    for i in range(len(mps) - 1):
        l = environ.read("L", i)
        r = environ.read("R", i + 1)
        l2 = environ_multi_mpo.read("L", i)
        r2 = environ_multi_mpo.read("R", i + 1)
        assert np.allclose(asnumpy(l), asnumpy(l2))
        assert np.allclose(asnumpy(r), asnumpy(r2))
Beispiel #18
0
def construct_mps_mpo_2(model, Mmax, nexciton, offset=Quantity(0)):
    """
    MPO/MPS structure 2
    e1,ph11,ph12,..e2,ph21,ph22,...en,phn1,phn2...
    """
    """
    initialize MPO
    """
    mpo = Mpo(model, offset=offset)
    """
    initialize MPS according to quantum number
    """
    mps = Mps.random(model, nexciton, Mmax, percent=1)
    # print("initialize left-canonical:", mps.check_left_canonical())

    return mps, mpo
Beispiel #19
0
def test_VibBasis(basistype):
    nv = 2
    pdim = 6
    hessian = np.array([[2, 1], [1, 3]])
    e, c = scipy.linalg.eigh(hessian)
    ham_terms = []
    basis = []
    for iv in range(nv):
        op = Op("p^2", f"v_{iv}", factor=0.5, qn=0)
        ham_terms.append(op)
        if basistype == "SineDVR":
            # sqrt(<x^2>) of the highest vibrational basis
            x_mean = np.sqrt((pdim + 0.5) / np.sqrt(hessian[iv, iv]))
            bas = Ba.BasisSineDVR(f"v_{iv}",
                                  2 * pdim,
                                  -x_mean * 1.5,
                                  x_mean * 1.5,
                                  endpoint=True)
            print("x_mean", x_mean, bas.dvr_x)
        else:
            if basistype == "SHO":
                dvr = False
            else:
                dvr = True
            bas = Ba.BasisSHO(f"v_{iv}",
                              np.sqrt(hessian[iv, iv]),
                              pdim,
                              dvr=dvr)

        basis.append(bas)
    for iv in range(nv):
        for jv in range(nv):
            op = Op("x x", [f"v_{iv}", f"v_{jv}"],
                    factor=0.5 * hessian[iv, jv],
                    qn=[0, 0])
            ham_terms.append(op)
    model = Model(basis, ham_terms)
    mpo = Mpo(model)
    mps = Mps.random(model, 0, 10)
    mps.optimize_config.nroots = 2
    energy, mps = gs.optimize_mps(mps, mpo)
    w1, w2 = np.sqrt(e)
    std = [(w1 + w2) * 0.5, w1 * 1.5 + w2 * 0.5]
    print(basistype, "calc:", energy[-1], "exact:", std)
    assert np.allclose(energy[-1], std)
Beispiel #20
0
def test_variational_compress(comp, mp):

    if mp == "mpo":
        mps = Mpo(holstein_model)
        M = 20
    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}")

    # 2site algorithm
    mps.compress_config.vprocedure = [[M, 1.0], [M, 0.2], [M, 0.1]] + [
        [M, 0],
    ] * 10
    mps.compress_config.vmethod = "2site"
    var_mps = mps.variational_compress(mpo, guess=None)
    dis = var_mps.distance(std_mps) / std_mps.dmrg_norm
    print(f"var2_mps: {var_mps}, dis: {dis}")
    assert np.allclose(dis, 0.0, atol=1e-4)
    assert np.allclose(var_mps.dmrg_norm, std_mps.dmrg_norm, atol=1e-4)

    # 1site algorithm with 2site result as a guess
    # 1site algorithm is easy to be trapped in a local minimum
    var_mps.compress_config.vprocedure = [
        [M, 0],
    ] * 10
    var_mps.compress_config.vmethod = "1site"
    var_mps = mps.variational_compress(mpo, guess=var_mps)
    dis = var_mps.distance(std_mps) / std_mps.dmrg_norm
    print(f"var1_mps: {var_mps}, dis: {dis}")
    assert np.allclose(dis, 0.0, atol=1e-4)
    assert np.allclose(var_mps.dmrg_norm, std_mps.dmrg_norm, atol=1e-4)
Beispiel #21
0
#h2e = 0.5*(h2e+h2e.transpose((2,3,0,1)))

model = qc_model(h1e, h2e)

order = {}
basis = []
for iorb in range(spin_norbs):
    order[f"e_{iorb}"] = iorb
    basis.append(ba.BasisHalfSpin(sigmaqn=[0, 1]))

mol_list2 = MolList2(order, basis, model, ModelTranslator.general_model)
mpo = Mpo(mol_list2)
logger.info(f"mpo_bond_dims:{mpo.bond_dims}")

nelec = 10
energy_list = {}
M = 50
procedure = [[M, 0.4], [M, 0.2], [M, 0.1], [M, 0], [M, 0], [M, 0], [M, 0]]
mps = Mps.random(mol_list2, nelec, M, percent=1.0)

mps.optimize_config.procedure = procedure
mps.optimize_config.method = "2site"
energies = solver.optimize_mps_dmrg(mps.copy(), mpo)
gs_e = energies.min() + nuc
logger.info(f"lowest energy: {gs_e}")
# fci result
assert np.allclose(gs_e, -75.008697516450)

end = time.time()
logger.info(f"time cost {end - start}")
Beispiel #22
0
 def init_mps(self):
     self.cv_mps = Mps.random(
         self.mol_list, 1-self.nexciton, self.m_max, percent=1.0)
Beispiel #23
0
def test_identity():
    identity = Mpo.identity(holstein_model)
    mps = Mps.random(holstein_model, nexciton=1, m_max=5)
    assert mps.expectation(identity) == pytest.approx(
        mps.dmrg_norm) == pytest.approx(1)
def test_H_chain_LDOS():
    # local density of states of four H_Chain
    # Ronca,J. Chem. Theory Comput. 2017, 13, 5560-5571
    # example to use Mollist2 to do CV calculation

    spatial_norbs = 4
    spin_norbs = spatial_norbs * 2
    h1e, h2e, nuc = h_qc.read_fcidump(
        os.path.join(cur_dir, "fcidump_lowdin_h4.txt"), spatial_norbs)

    basis, ham_terms = h_qc.qc_model(h1e, h2e)

    model = Model(basis, ham_terms)
    mpo = Mpo(model)

    nelec = spatial_norbs
    M = 50
    procedure = [[M, 0.4], [M, 0.2]] + [
        [M, 0],
    ] * 6
    mps = Mps.random(model, nelec, M, percent=1.0)

    mps.optimize_config.procedure = procedure
    mps.optimize_config.method = "2site"
    energies, mps = gs.optimize_mps(mps, mpo)
    gs_e = min(energies) + nuc

    assert np.allclose(gs_e, -2.190384218792706)
    mps_e = mps.expectation(mpo)

    def photoelectron_operator(idx):
        # norbs is the spin orbitals
        # green function
        op_list = [Op("sigma_z", iorb, qn=0) for iorb in range(idx)]
        return Op.product(op_list + [Op("sigma_+", idx, qn=-1)])

    dipole_model = photoelectron_operator(nelec - 1)
    dipole_op = Mpo(model, dipole_model)
    b_mps = dipole_op.apply(mps)

    #std
    #test_freq = np.linspace(0.25, 1.25, 100, endpoint=False).tolist()
    test_freq = np.linspace(0.25, 1.25, 20, endpoint=False).tolist()
    eta = 0.05
    M = 10
    procedure_cv = [0.4, 0.2] + [0] * 6
    spectra = SpectraZtCV(model,
                          None,
                          M,
                          eta,
                          h_mpo=mpo,
                          method="2site",
                          procedure_cv=procedure_cv,
                          b_mps=b_mps.scale(-eta),
                          e0=mps_e)

    result = batch_run(test_freq, 1, spectra)
    std = np.load(os.path.join(cur_dir, "H_chain_std.npy"))
    #np.save("res", result)
    #np.save("freq", test_freq)
    assert np.allclose(result, std[::5])
Beispiel #25
0
 def get_imps(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)
     return i_mps
Beispiel #26
0
 def get_imps(self):
     mmax = self.optimize_config.procedure[0][0]
     i_mps = Mps.random(self.h_mpo.mol_list, self.nexciton, mmax, 1)
     i_mps.optimize_config = self.optimize_config
     solver.optimize_mps(i_mps, self.h_mpo)
     return i_mps
Beispiel #27
0
def test_tda():
    from renormalizer.tests.c2h4_para import ff, omega_std, B, zeta
    # See  J. Chem. Phys. 153, 084118 (2020) for the details of the Hamiltonian
    # the order is according to the harmonic frequency from small to large.
    ham_terms = []

    nmode = 12
    omega = {}

    nterms = 0
    # potential terms
    for term in ff:
        mode, factor = term[:-1], term[-1]
        # ignore the factor smaller than 1e-15
        if abs(factor) < 1e-15:
            continue
        # ignore the expansion larger than 4-th order
        #if len(mode) > 4:
        #    continue

        mode = Counter(mode)

        # the permutation symmetry prefactor
        prefactor = 1.
        for p in mode.values():
            prefactor *= scipy.special.factorial(p, exact=True)

        # check harmonic term
        if len(mode) == 1 and list(mode.values())[0] == 2:
            omega[list(mode.keys())[0]] = np.sqrt(factor)

        dof = [f"v_{i}" for i in mode.keys()]
        symbol = " ".join([f"x^{i}" for i in mode.values()])
        qn = [0 for i in mode.keys()]
        factor /= prefactor
        ham_terms.append(Op(symbol, dof, factor=factor, qn=qn))
        nterms += 1

    # Coriolis terms
    B = np.array(B)
    zeta = np.array(zeta)

    terms = [("x", "partialx", "x", "partialx", 1.),
             ("x", "partialx", "partialx", "x", -1.),
             ("partialx", "x", "x", "partialx", -1.),
             ("partialx", "x", "partialx", "x", 1.)]
    for j, l in itertools.product(range(nmode), repeat=2):
        for i, k in itertools.product(range(j), range(l)):
            dof = [f"v_{i}", f"v_{j}", f"v_{k}", f"v_{l}"]
            tmp = -np.einsum("i,i,i ->", B, zeta[:, i, j], zeta[:, k, l])
            qn = [0, 0, 0, 0]
            if abs(tmp) < 1e-15:
                continue
            for term in terms:
                symbol, factor = " ".join(term[:-1]), term[-1] * tmp
                ham_terms.append(Op(symbol, dof, factor=factor, qn=qn))
            nterms += 4

    # Kinetic terms
    for imode in range(nmode):
        ham_terms.append(Op("p^2", f"v_{imode}", 0.5, 0))
        nterms += 1

    logger.info(f"nterms: {nterms}")
    logger.info(
        f"omega: {np.sort(np.array(list(omega.values())),axis=None)*au2cm}")
    logger.info(f"omega_std: {np.array(omega_std)}")

    basis = []
    for imode in range(nmode):
        basis.append(ba.BasisSHO(f"v_{imode}", omega[imode], 4, dvr=False))

    model = Model(basis, ham_terms)
    mpo = Mpo(model)
    logger.info(f"mpo_bond_dims:{mpo.bond_dims}")
    #assert mpo.is_hermitian()

    alias = [
        "v10", "v8", "v7", "v4", "v6", "v3", "v12", "v2", "v11", "v1", "v5",
        "v9"
    ]
    energy_list = {}
    M = 10
    procedure = [[M, 0.4], [M, 0.2], [M, 0.2], [M, 0.1]] + [[M, 0]] * 100
    mps = Mps.random(model, 0, M, percent=1.0)
    mps.optimize_config.procedure = procedure
    mps.optimize_config.method = "2site"
    mps.optimize_config.e_rtol = 1e-6
    mps.optimize_config.e_atol = 1e-8
    mps.optimize_config.nroots = 1
    energies, mps = gs.optimize_mps(mps, mpo)
    logger.info(f"M: {M}, energy : {np.array(energies[-1])*au2cm}")
    tda = TDA(model, mpo, mps, nroots=3, algo="primme")
    e = tda.kernel(include_psi0=False)
    logger.info(f"tda energy : {(e-energies[-1])*au2cm}")
    assert np.allclose((e - energies[-1]) * au2cm,
                       [824.74925026, 936.42650242, 951.96826289],
                       atol=1)
    config, compressed_mps = tda.analysis_dominant_config(alias=alias)
    # std is calculated with M=200, include_psi0=True; the initial gs is
    # calculated with 9 state SA-DMRG; physical_bond=6
    std = np.load(os.path.join(cur_dir, "c2h4_std.npz"))["200"]
    assert np.allclose(energies[-1] * au2cm, std[0], atol=2)
    assert np.allclose(e * au2cm, std[1:4], atol=3)
Beispiel #28
0
def find_lowest_energy(h_mpo: Mpo, nexciton, Mmax):
    logger.debug("begin finding lowest energy")
    model = h_mpo.model
    mps = Mps.random(model, nexciton, Mmax)
    energies, _ = optimize_mps(mps, h_mpo)
    return energies[-1]
Beispiel #29
0
def test_identity():
    identity = Mpo.identity(mol_list)
    mps = Mps.random(mol_list, nexciton=1, m_max=5)
    assert mps.expectation(identity) == pytest.approx(mps.dmrg_norm) == pytest.approx(1)
Beispiel #30
0
    h1e = np.random.uniform(-1, 1, size=(spin_norbs, spin_norbs))
    h2e = np.random.uniform(-1,
                            1,
                            size=(spin_norbs, spin_norbs, spin_norbs,
                                  spin_norbs))
    h1e = 0.5 * (h1e + h1e.T)
    h2e = 0.5 * (h2e + h2e.transpose((2, 3, 0, 1)))

basis, ham_terms = h_qc.qc_model(h1e, h2e)

model = Model(basis, ham_terms)
mpo = Mpo(model)
logger.info(f"mpo_bond_dims:{mpo.bond_dims}")

nelec = 10
energy_list = {}
M = 50
procedure = [[M, 0.4], [M, 0.2], [M, 0.1], [M, 0], [M, 0], [M, 0], [M, 0]]
mps = Mps.random(model, nelec, M, percent=1.0)

mps.optimize_config.procedure = procedure
mps.optimize_config.method = "2site"
energies, mps = gs.optimize_mps(mps.copy(), mpo)
gs_e = min(energies) + nuc
logger.info(f"lowest energy: {gs_e}")
# fci result
assert np.allclose(gs_e, -75.008697516450)

end = time.time()
logger.info(f"time cost {end - start}")