Path: blob/main/chemistry-simulations/aux_files/krylov/qchem/classical_pyscf.py
583 views
# ============================================================================ #1# Copyright (c) 2024 - 2025 NVIDIA Corporation & Affiliates. #2# All rights reserved. #3# #4# This source code and the accompanying materials are made available under #5# the terms of the Apache License 2.0 which accompanies this distribution. #6# ============================================================================ #78import json9from functools import reduce1011try:12import numpy as np13except ValueError:14print('numpy should be installed.')1516try:17from pyscf import gto, scf, cc, ao2mo, mp, mcscf, fci18except ValueError:19print('PySCF should be installed. Use pip install pyscf')2021from pyscf.tools import molden2223#######################################################24# Generate the spin molecular orbital hamiltonian252627def generate_molecular_spin_ham_restricted(h1e, h2e, ecore):2829# This function generates the molecular spin Hamiltonian30# H = E_core+sum_{`pq`} h_{`pq`} a_p^dagger a_q +31# 0.5 * h_{`pqrs`} a_p^dagger a_q^dagger a_r a_s32# h1e: one body integrals h_{`pq`}33# h2e: two body integrals h_{`pqrs`}34# `ecore`: constant (nuclear repulsion or core energy in the active space Hamiltonian)3536# Total number of qubits equals the number of spin molecular orbitals37nqubits = 2 * h1e.shape[0]3839# Initialization40one_body_coeff = np.zeros((nqubits, nqubits))41two_body_coeff = np.zeros((nqubits, nqubits, nqubits, nqubits))4243ferm_ham = []4445for p in range(nqubits // 2):46for q in range(nqubits // 2):4748# p & q have the same spin <a|a>= <b|b>=149# <a|b>=<b|a>=0 (orthogonal)50one_body_coeff[2 * p, 2 * q] = h1e[p, q]51temp = str(h1e[p, q]) + ' a_' + str(p) + '^dagger ' + 'a_' + str(q)52ferm_ham.append(temp)53one_body_coeff[2 * p + 1, 2 * q + 1] = h1e[p, q]54temp = str(h1e[p, q]) + ' b_' + str(p) + '^dagger ' + 'b_' + str(q)55ferm_ham.append(temp)5657for r in range(nqubits // 2):58for s in range(nqubits // 2):5960# Same spin (`aaaa`, `bbbbb`) <a|a><a|a>, <b|b><b|b>61two_body_coeff[2 * p, 2 * q, 2 * r,622 * s] = 0.5 * h2e[p, q, r, s]63temp = str(0.5 * h2e[p, q, r, s]) + ' a_' + str(64p) + '^dagger ' + 'a_' + str(65q) + '^dagger ' + 'a_' + str(r) + ' a_' + str(s)66ferm_ham.append(temp)67two_body_coeff[2 * p + 1, 2 * q + 1, 2 * r + 1,682 * s + 1] = 0.5 * h2e[p, q, r, s]69temp = str(0.5 * h2e[p, q, r, s]) + ' b_' + str(70p) + '^dagger ' + 'b_' + str(71q) + '^dagger ' + 'b_' + str(r) + ' b_' + str(s)72ferm_ham.append(temp)7374# Mixed spin(`abab`, `baba`) <a|a><b|b>, <b|b><a|a>75#<a|b>= 0 (orthogonal)76two_body_coeff[2 * p, 2 * q + 1, 2 * r + 1,772 * s] = 0.5 * h2e[p, q, r, s]78temp = str(0.5 * h2e[p, q, r, s]) + ' a_' + str(79p) + '^dagger ' + 'a_' + str(80q) + '^dagger ' + 'b_' + str(r) + ' b_' + str(s)81ferm_ham.append(temp)82two_body_coeff[2 * p + 1, 2 * q, 2 * r,832 * s + 1] = 0.5 * h2e[p, q, r, s]84temp = str(0.5 * h2e[p, q, r, s]) + ' b_' + str(85p) + '^dagger ' + 'b_' + str(86q) + '^dagger ' + 'a_' + str(r) + ' a_' + str(s)87ferm_ham.append(temp)8889full_hamiltonian = " + ".join(ferm_ham)9091return one_body_coeff, two_body_coeff, ecore, full_hamiltonian929394####################################################################9596# A- Gas phase simulation97#############################98## Beginning of simulation99#############################100101def get_mol_hamiltonian(xyz:str, spin:int, charge: int, basis:str, symmetry:bool = False, memory:float = 4000, cycles:int = 100, \102initguess:str = 'minao', nele_cas = None, norb_cas = None, MP2:bool = False, natorb:bool = False,\103casci:bool = False, ccsd:bool = False, casscf:bool = False, integrals_natorb:bool = False, \104integrals_casscf:bool = False, viz_orb:bool = False, verbose:bool = False):105################################106# Initialize the molecule107################################108filename = xyz.split('.')[0]109110if (nele_cas is None) and (norb_cas is not None):111raise ValueError(112"WARN: nele_cas is None and norb_cas is not None. "113"nele_cas and norb_cas should be either both None or have values")114115if (nele_cas is not None) and (norb_cas is None):116raise ValueError(117"WARN: nele_cas is not None and norb_cas is None. "118"nele_cas and norb_cas should be either both None or have values")119120########################################################################121# To add (coming soon)122123mol = gto.M(atom=xyz,124spin=spin,125charge=charge,126basis=basis,127max_memory=memory,128symmetry=symmetry,129output=filename + '-pyscf.log',130verbose=4)131132##################################133# Mean field (HF)134##################################135136if spin == 0:137myhf = scf.RHF(mol)138myhf.max_cycle = cycles139myhf.chkfile = filename + '-pyscf.chk'140myhf.init_guess = initguess141myhf.kernel()142143norb = myhf.mo_coeff.shape[1]144if verbose:145print('[pyscf] Total number of orbitals = ', norb)146else:147myhf = scf.ROHF(mol)148myhf.max_cycle = cycles149myhf.chkfile = filename + '-pyscf.chk'150myhf.init_guess = initguess151myhf.kernel()152153norb = myhf.mo_coeff[0].shape[1]154if verbose:155print('[pyscf] Total number of orbitals = ', norb)156157nelec = mol.nelectron158if verbose:159print('[pyscf] Total number of electrons = ', nelec)160if verbose:161print('[pyscf] HF energy = ', myhf.e_tot)162if viz_orb:163molden.from_mo(mol, filename + '_HF_molorb.molden', myhf.mo_coeff)164165##########################166# MP2167##########################168if MP2:169170if spin != 0:171raise ValueError("WARN: ROMP2 is unvailable in pyscf.")172else:173mymp = mp.MP2(myhf)174mp_ecorr, mp_t2 = mymp.kernel()175if verbose:176print('[pyscf] R-MP2 energy= ', mymp.e_tot)177178if integrals_natorb or natorb:179# Compute natural orbitals180noons, natorbs = mcscf.addons.make_natural_orbitals(mymp)181if verbose:182print(183'[pyscf] Natural orbital occupation number from R-MP2: '184)185if verbose:186print(noons)187if viz_orb:188molden.from_mo(mol, filename + '_MP2_natorb.molden',189natorbs)190191#######################################192# CASCI if active space is defined193# FCI if the active space is None194######################################195if casci:196197if nele_cas is None:198myfci = fci.FCI(myhf)199result = myfci.kernel()200if verbose:201print('[pyscf] FCI energy = ', result[0])202203else:204if natorb and (spin == 0):205mycasci = mcscf.CASCI(myhf, norb_cas, nele_cas)206mycasci.kernel(natorbs)207if verbose:208print('[pyscf] R-CASCI energy using natural orbitals= ',209mycasci.e_tot)210211elif natorb and (spin != 0):212raise ValueError(213"WARN: Natural orbitals cannot be computed. ROMP2 is unvailable in pyscf."214)215216else:217mycasci_mo = mcscf.CASCI(myhf, norb_cas, nele_cas)218mycasci_mo.kernel()219if verbose:220print('[pyscf] R-CASCI energy using molecular orbitals= ',221mycasci_mo.e_tot)222223########################224# CCSD225########################226if ccsd:227228if nele_cas is None:229mycc = cc.CCSD(myhf)230mycc.max_cycle = cycles231mycc.kernel()232if verbose:233print('[pyscf] Total R-CCSD energy = ', mycc.e_tot)234235else:236mc = mcscf.CASCI(myhf, norb_cas, nele_cas)237frozen = []238frozen += [y for y in range(0, mc.ncore)]239frozen += [240y for y in range(mc.ncore + norb_cas, len(myhf.mo_coeff))241]242if natorb and (spin == 0):243mycc = cc.CCSD(myhf, frozen=frozen, mo_coeff=natorbs)244mycc.max_cycle = cycles245mycc.kernel()246if verbose:247print(248'[pyscf] R-CCSD energy of the active space using natural orbitals= ',249mycc.e_tot)250251elif natorb and (spin != 0):252raise ValueError(253"WARN: Natural orbitals cannot be computed. ROMP2 is unvailable in pyscf."254)255256else:257mycc = cc.CCSD(myhf, frozen=frozen)258mycc.max_cycle = cycles259mycc.kernel()260if verbose:261print(262'[pyscf] R-CCSD energy of the active space using molecular orbitals= ',263mycc.e_tot)264265#########################266# CASSCF267#########################268if casscf:269if nele_cas is None:270raise ValueError("WARN: You should define the active space.")271272if natorb and (spin == 0):273mycas = mcscf.CASSCF(myhf, norb_cas, nele_cas)274mycas.max_cycle_macro = cycles275mycas.kernel(natorbs)276if verbose:277print('[pyscf] R-CASSCF energy using natural orbitals= ',278mycas.e_tot)279280elif natorb and (spin != 0):281raise ValueError(282"WARN: Natural orbitals cannot be computed. ROMP2 is unvailable in pyscf."283)284285else:286mycas = mcscf.CASSCF(myhf, norb_cas, nele_cas)287mycas.max_cycle_macro = cycles288mycas.kernel()289if verbose:290print('[pyscf] R-CASSCF energy using molecular orbitals= ',291mycas.e_tot)292293###################################294# CASCI: `FCI` of the active space295##################################296if casci and casscf:297298if natorb and (spin != 0):299raise ValueError(300"WARN: Natural orbitals cannot be computed. ROMP2 is unavailable in pyscf."301)302else:303h1e_cas, ecore = mycas.get_h1eff()304h2e_cas = mycas.get_h2eff()305306e_fci, fcivec = fci.direct_spin1.kernel(h1e_cas,307h2e_cas,308norb_cas,309nele_cas,310ecore=ecore)311if verbose:312print('[pyscf] R-CASCI energy using the casscf orbitals= ',313e_fci)314315###################################################################################316# Computation of one- and two- electron integrals for the active space Hamiltonian317###################################################################################318319if nele_cas is None:320# Compute the 1e integral in atomic orbital then convert to HF basis321h1e_ao = mol.intor("int1e_kin") + mol.intor("int1e_nuc")322## Ways to convert from `ao` to mo323#h1e=`np.einsum('pi,pq,qj->ij', myhf.mo_coeff, h1e_ao, myhf.mo_coeff)`324h1e = reduce(np.dot, (myhf.mo_coeff.T, h1e_ao, myhf.mo_coeff))325#h1e=`reduce(np.dot, (myhf.mo_coeff.conj().T, h1e_ao, myhf.mo_coeff))`326327# Compute the 2e integrals then convert to HF basis328h2e_ao = mol.intor("int2e_sph", aosym='1')329h2e = ao2mo.incore.full(h2e_ao, myhf.mo_coeff)330331# `Reorder the chemist notation (pq|rs) ERI h_prqs to h_pqrs`332# a_p^dagger a_r a_q^dagger a_s --> a_p^dagger a_q^dagger a_r a_s333h2e = h2e.transpose(0, 2, 3, 1)334335nuclear_repulsion = myhf.energy_nuc()336337# Compute the molecular spin electronic Hamiltonian from the338# molecular electron integrals339obi, tbi, e_nn, ferm_ham = generate_molecular_spin_ham_restricted(340h1e, h2e, nuclear_repulsion)341342else:343344if integrals_natorb:345if spin != 0:346raise ValueError("WARN: ROMP2 is unvailable in pyscf.")347else:348mc = mcscf.CASCI(myhf, norb_cas, nele_cas)349h1e_cas, ecore = mc.get_h1eff(natorbs)350h2e_cas = mc.get_h2eff(natorbs)351h2e_cas = ao2mo.restore('1', h2e_cas, norb_cas)352h2e_cas = np.asarray(h2e_cas.transpose(0, 2, 3, 1), order='C')353354elif integrals_casscf:355if casscf:356h1e_cas, ecore = mycas.get_h1eff()357h2e_cas = mycas.get_h2eff()358h2e_cas = ao2mo.restore('1', h2e_cas, norb_cas)359h2e_cas = np.asarray(h2e_cas.transpose(0, 2, 3, 1), order='C')360else:361raise ValueError(362"WARN: You need to run casscf. Use casscf=True.")363364else:365mc = mcscf.CASCI(myhf, norb_cas, nele_cas)366h1e_cas, ecore = mc.get_h1eff(myhf.mo_coeff)367h2e_cas = mc.get_h2eff(myhf.mo_coeff)368h2e_cas = ao2mo.restore('1', h2e_cas, norb_cas)369h2e_cas = np.asarray(h2e_cas.transpose(0, 2, 3, 1), order='C')370371# Compute the molecular spin electronic Hamiltonian from the372# molecular electron integrals373obi, tbi, core_energy, ferm_ham = generate_molecular_spin_ham_restricted(374h1e_cas, h2e_cas, ecore)375376# Dump obi and `tbi` to binary file.377# `obi.astype(complex).tofile(f'{filename}_one_body.dat')`378# `tbi.astype(complex).tofile(f'{filename}_two_body.dat')`379380######################################################381# Dump energies / etc to a metadata file382if nele_cas is None:383# `metadata = {'num_electrons': nelec, 'num_orbitals': norb, 'nuclear_energy': e_nn, 'hf_energy': myhf.e_tot}`384# with open(f'{filename}_metadata.`json`', 'w') as f:385# `json`.dump(metadata, f)386return (obi, tbi, e_nn, nelec, norb, ferm_ham)387388else:389# `metadata = {'num_electrons_cas': nele_cas, 'num_orbitals_cas': norb_cas, 'core_energy': ecore, 'hf_energy': myhf.e_tot}`390# with open(f'{filename}_metadata.`json`', 'w') as f:391# `json`.dump(metadata, f)392return (obi, tbi, ecore, nele_cas, norb_cas, ferm_ham)393394395#######################################################396397398