Skip to content

BUG: Discrepancy between least squares results #122

Closed

Description

@kzsigmond

Describe the problem:

I noticed a discrepancy in the results for the AP1roGSDGeneralized sen-o wavefunction. When optimized with the least squares method using the interface to PyCI, I get two different answers depending on whether I use the jacobian from the interface or the 2-point approximation from scipy. The latter is the default method if use_jac=False. The approximate jacobian is the closest to the old results @rugwed-lokhande has. The difference between approximate and exact jacobian is around 14 mHartree.

Code that reproduces the bug:

Setup for the calculation without Jacobian:

# PySCF Calculation
import time
from pyscf import gto, scf
import numpy as np

from fanpy.wfn.cc.ap1rog_generalized_NEW import AP1roGSDGeneralized
from fanpy.ham.restricted_chemical import RestrictedMolecularHamiltonian
from fanpy.tools.sd_list import sd_list
import fanpy.interface as interface


# Record the start time
start_time = time.time()

energy = {}

print('# PySCF calculation for alpha = 20.0...')
mol = gto.M(atom = [['Be', ['0.0', '0.0', '0.0']], ['H', ['0.0', '-0.7', '20.0']], ['H', ['0.0', '0.7', '20.0']]],
            unit = 'B',
            basis = 'sto-6g')

myhf = scf.HF(mol)
myhf.kernel()
new_guess, _, stable, _ = myhf.stability(return_status=True)

if not stable:
    print("HF stability failed, trying Newton method...")
    myhf = myhf.newton().run(new_guess, myhf.mo_occ)
    _, _, stable, _ =  myhf.stability(return_status=True)

if not stable:
    raise RuntimeError("HF stability failed")
# PySCF interface
pyscf_interface = interface.pyscf.PYSCF(myhf)

print("\n>> >> Fanpy")

   
# Number of electrons
print("Number of Electrons: {}".format(pyscf_interface.nelec))

# Number of spin orbitals
print("Number of Spin Orbitals: {}".format(pyscf_interface.nspinorb))

# Nuclear-nuclear repulsion
print("Nuclear-nuclear repulsion: {}".format(pyscf_interface.energy_nuc))

# Fanpy calculation
print('# Fanpy calculation for alpha = 1.0...')

# Initialize wavefunction
wfn = AP1roGSDGeneralized(pyscf_interface.nelec,
                          pyscf_interface.nspinorb,
                          params=None,
                          memory=None,
                          ranks=None,
                          indices=None,
                          refwfn=None,
                          s_type='sen-o',
                          exop_combinations=None)
wfn.assign_params(wfn.params + 0.0001 * 2 * (np.random.rand(*wfn.params.shape) - 0.5))
print('Wavefunction: AP1roGSDGeneralized (sen-o)')
print(f"Number of parameters: {wfn.nparams}")

# Initialize Hamiltonian
ham = RestrictedMolecularHamiltonian(pyscf_interface.one_int, pyscf_interface.two_int, update_prev_params=True)
print('Hamiltonian: RestrictedMolecularHamiltonian')

# Projection space
exc_orders = [1, 2, 3, 4, 5, 6]
pspace = sd_list(pyscf_interface.nelec, pyscf_interface.nspinorb, num_limit=None, exc_orders=exc_orders, spin=0)
nproj = len(pspace)
print("Projection space (orders of excitations): ", exc_orders)
print("Number of projections (CISD): {:}".format(nproj))

print("\n# New interface!")
# Initialize objective
from fanpy.eqn.projected import ProjectedSchrodinger

objective = ProjectedSchrodinger(wfn, ham, energy_type="compute", pspace=pspace)

pyci_interface = interface.pyci.PYCI(objective, pyscf_interface.energy_nuc, legacy_fanci=False)
results = pyci_interface.objective.optimize(
    np.hstack([wfn.params, pyscf_interface.energy_elec]), mode="lstsq", use_jac=False, xtol=1e-3, gtol=1e-3, ftol=1e-3
)
# Record the end time
end_time = time.time()

# Calculate the total time taken
execution_time = end_time - start_time

print(f'Total execution time: {execution_time} seconds')
print("### Final energy: {:}".format(results["energy"] + pyscf_interface.energy_nuc))

print("results[success]: ", results["success"])
print("results[message]: ", results["message"])

The only change for the calculation with the jacobian was the optimize step:

results = pyci_interface.objective.optimize(
    np.hstack([wfn.params, pyscf_interface.energy_elec]), mode="lstsq", use_jac=True
)

Traceback error:

Both calculations finish the least squares optimization successfully.

current code with jacobian: -15.681482631037682
current code without jacobian: -15.695062328095915
old calculation for FanPT paper: -15.695097095236044 (from @rugwed-lokhande)

Package versions:

python: 3.10.16
fanpy: 1.0.0 (branch for #102)
numpy: 1.26.4
scipy: 1.15.2

Additonal comments

No response

Activity

  1. added theissue type on Aug 27, 2025
  2. added
    bugSomething isn't working
    triageNeeds to be assigned/validated
    on Aug 27, 2025
  3. added this to the v2.0.0 milestone on Aug 29, 2025
  4. self-assigned this
    on Aug 29, 2025
  5. added
    backlogPlanned but not started
    and removed
    triageNeeds to be assigned/validated
    on Aug 29, 2025
  6. kzsigmond commented on Nov 17, 2025

    @kzsigmond
    ContributorAuthor

    An issue for the alpha 20 geometry was the way we generate the s-space in the new interface. In the old interface, we passed seniority=0 to convert to fanci. In the new interface, the seniority seems to be determined by the fanpy wavefunction. If I manually set the seniority to 0, I get the same results as the old interface had as well (even though the wfn is not a seniority 0 wfn). I have created issue #165 for this, since I am not sure if the s-space generation is the only cause for the discrepancy between the least squares issue.

  7. linked a pull request that will close this issueFix: chunking in the new interface #168on Nov 19, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

backlogPlanned but not startedbugSomething isn't working

Type

Projects

No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions