tequilahub / tequila

A High-Level Abstraction Framework for Quantum Algorithms
MIT License
362 stars 101 forks source link

tq.MoleculeFromOpenFermion fails in get_geometry due to Type of geometry (list) #310

Closed salperinlea closed 11 months ago

salperinlea commented 11 months ago

Describe the bug attempts to get tequila "molecule" objects via the tq.MoleculeFromOpenFermion fail due to an error essentially related to typing. OpenFermion accepts initialization of MolecularData objects via lists of lists of (string, list) as geometries (that is, xyzfiles can be bypassed), but tequila is expecting a string to be the source of the geometry, leading to an error when the .split method is invoked. Stacktrace below:

File ~/anaconda3/envs/use/lib/python3.8/site-packages/tequila/quantumchemistry/__init__.py:177, in MoleculeFromOpenFermion(molecule, transformation, backend, *args, **kwargs)
    162 """
    163 Initialize a tequila Molecule directly from an openfermion molecule object
    164 Parameters
   (...)
    174     The tequila molecule
    175 """
    176 if backend is None:
--> 177     return QuantumChemistryBase.from_openfermion(molecule=molecule, transformation=transformation, *args, **kwargs)
    178 else:
    179     INSTALLED_QCHEMISTRY_BACKENDS[backend].from_openfermion(molecule=molecule, transformation=transformation, *args,
    180                                                             **kwargs)

File ~/anaconda3/envs/use/lib/python3.8/site-packages/tequila/quantumchemistry/qc_base.py:169, in QuantumChemistryBase.from_openfermion(cls, molecule, transformation, *args, **kwargs)
    153 @classmethod
    154 def from_openfermion(cls, molecule: openfermion.MolecularData,
    155                      transformation: typing.Union[str, typing.Callable] = None,
    156                      *args,
    157                      **kwargs):
    158     """
    159     Initialize direclty from openfermion MolecularData object
    160 
   (...)
    167         The Tequila molecule
    168     """
--> 169     parameters = ParametersQC(basis_set=molecule.basis, geometry=molecule.geometry,
    170                               description=molecule.description, multiplicity=molecule.multiplicity,
    171                               charge=molecule.charge)
    172     return cls(parameters=parameters, transformation=transformation, molecule=molecule, *args, **kwargs)

File <string>:10, in __init__(self, basis_set, geometry, description, multiplicity, charge, name, frozen_core)

File ~/anaconda3/envs/use/lib/python3.8/site-packages/tequila/quantumchemistry/chemistry_tools.py:181, in ParametersQC.__post_init__(self, *args, **kwargs)
    179         self.description = description
    180 else:
--> 181     atoms = self.get_atoms()
    182     atom_names = sorted(list(set(atoms)), key=lambda x: self.get_atom_number(x), reverse=True)
    183     if self.name is None:

File ~/anaconda3/envs/use/lib/python3.8/site-packages/tequila/quantumchemistry/chemistry_tools.py:166, in ParametersQC.get_atoms(self)
    165 def get_atoms(self):
--> 166     return [x[0] for x in self.get_geometry()]

File ~/anaconda3/envs/use/lib/python3.8/site-packages/tequila/quantumchemistry/chemistry_tools.py:296, in ParametersQC.get_geometry(self)
    280 def get_geometry(self):
    281     """Returns the geometry
    282     If a xyz filename was given the file is read out
    283     otherwise it is assumed that the geometry was given as string
   (...)
    294 
    295     """
--> 296     if self.geometry.split('.')[-1] == 'xyz':
    297         geomstring, comment = self.read_xyz_from_file(self.geometry)
    298         if self.description == '':

AttributeError: 'list' object has no attribute 'split'

To Reproduce Steps to reproduce the behavior: Ideally an executable code snipped like

import tequila as tq
import openfermion as of

geo=[['Li',[0,0,0]]]
basis="sto-6g"
charge=0
multiplicity=2
mol=of.MolecularData(geometry=geo,basis=basis,charge=charge,multiplicity=multiplicity)
tmol=tq.MoleculeFromOpenFermion(mol)

Expected behavior The above code should run without issue, as should any further uses of the object created thereby.

Computer : OS is Arch Linux, latest kernel.

tequila version: 1.8.9 python version: 3.8.17 | packaged by conda-forge | (default, Jun 16 2023, 07:06:00) [GCC 11.4.0] platform: #1 ZEN SMP PREEMPT_DYNAMIC Thu, 21 Sep 2023 12:54:31 +0000

salperinlea commented 11 months ago

To clarify use case, the reason for initializing in this fashion is due to the necessity of include multiplicity info. An attempt to directly manifest a tq.Molecule object for atoms like lithium will fail due to spin;

tmol=tq.Molecule("Li 0 0 0",basis_set=basis,backend='pyscf') 

will fail with relevant traceback

RuntimeError: Electron number 3 and spin 0 are not consistent
Note mol.spin = 2S = Nalpha - Nbeta, not 2S+1
kottmanj commented 11 months ago

Hi Sumner, thanks for noticing. I forgot that the function exists. Should be fixable, but will take some time.

It is probably more convenient to initialize a charged molecule and construct circuits accordingly in the symmetry sector you aim for. Below this is illustrated of lithium. Setting the charge affects only the standard methods and interface to classical methods (that would not be available no matter what, as they all require closed-shell).

import tequila as tq
import numpy

geom="Li 0.0 0.0 0.0"
dummy = tq.Molecule(geometry=geom, basis_set="sto-6g", charge=1, frozen_core=False)

# not necessary
# switch to orthonormalized atomic orbitals
# will most likely have little to no effect on Li
dummy = dummy.use_native_orbitals()

# charge does not affect 2nd quantized Hamiltonian
H = dummy.make_hamiltonian()

v,vv = numpy.linalg.eigh(H.to_matrix())
print("Li ground state")
print(tq.QubitWaveFunction(vv[:,0]))

# can not use: dummy.prepare_reference (will create the charge+1 reference)
# should not use: dummy.make_ansatz() | all predefined ansatz schemes are taylored for closed-shell

# can still do manual stuff

U = tq.gates.X([0,1,2])
U+= dummy.UC(0,3,angle="a")
U+= dummy.make_excitation_gate(indices=[(0,4),(1,3),(2,6)],angle="b")

print(tq.simulate(U, variables={"a":1.0, "b":1.0, "c":1.0}))
salperinlea commented 11 months ago

Thanks for the quick reply Jacob. Above example works, but errors out if charge is set to 0, as expected.

My ultimate goal is to get UCC circuits with CCSD amplitudes for a swath of neutral atoms; I think this should be possible by a mix of tequila code and some hand-crafting from pyscf directly.

kottmanj commented 11 months ago

Yes, that would be the way to go. Get amplitudes from pyscf and circuits from tq. For the circuits it doesn't matter what the charge is, just make sure to create the right initial state.

Thats why charge is set to one in the example above. You create a dummy molecule that is closed shell, but otherwise the same. In the example above, the ground state is the neutral ground state and the circuit creates a 3 electron wavefunction.

kottmanj commented 11 months ago

I put it on the todo, to allow for general charges and multiplicities, but it will take a while.