Skip to content

Add analytic nuclear gradients for ROHF-CASCI and GBCI - #208

Merged
MatthewRHermes merged 12 commits into
pyscf:masterfrom
qchemlab-snu:master
Aug 25, 2026
Merged

MatthewRHermes merged 12 commits into
pyscf:masterfrom
qchemlab-snu:master

Conversation

@jiseong0626

Copy link
Copy Markdown
Contributor

Summary

This PR adds analytic nuclear-gradient implementations for both ROHF-CASCI and grouped-bath configuration interaction (GBCI).

The ROHF-CASCI implementation provides the orbital-response framework required for CASCI calculations based on ROHF orbitals. This framework is further extended to ROHF-based GBCI calculations, including the additional response contributions arising from group-dependent bath-orbital relaxation.

Main changes

ROHF-CASCI gradients

  • pyscf/grad/rohf_casci.py
    • Implements analytic nuclear gradients for CASCI calculations based on an ROHF reference.
    • Constructs the nonredundant ROHF orbital-response space.
    • Solves the ROHF adjoint equation using a matrix-free iterative solver.
    • Provides reusable ROHF response utilities for the GBCI gradient implementation.

GBCI gradients

  • pyscf/grad/gbci.py

    • Implements analytic GBCI nuclear gradients for restricted references.
    • Includes group-dependent bath-orbital relaxation through coupled-perturbed equations.
    • Provides gradient scanner support.
    • Dispatches ROHF-reference calculations to the ROHF-specific implementation.
  • pyscf/grad/rohf_gbci.py

    • Extends the GBCI gradient implementation to ROHF references.
    • Reuses the ROHF-CASCI adjoint-response framework.

GBCI interface and solver updates

  • pyscf/gbci/gbci.py

    • Adds nuc_grad_method() to the GBCI interface.
    • Adds an energy scanner for geometry-dependent calculations.
    • Caches the orbital, overlap, density, and core-energy intermediates required by the gradient code.
  • pyscf/gbci/fasscf.py

    • Projects the ROHF orbital gradient onto the inactive/bath orbital-rotation space.
    • Uses the projected gradient in FASSCF convergence checks.
    • Updates the convergence reporting for bath-orbital optimization.
  • pyscf/gbci/direct_gbci.py

    • Improves Davidson initialization when fewer initial vectors than requested roots are supplied.

Tests and examples

  • pyscf/grad/test/test_gbci_grad.py

    • Adds gradient regression tests for:
      • Singlet LiH with an RHF reference
      • Singlet LiF with an RHF reference
      • Triplet LiH with an ROHF reference
      • Triplet LiF with an ROHF reference
  • examples/grad/30-gbci_gradient.py

    • Adds an example demonstrating the GBCI nuclear-gradient interface.
  • pyscf/gbci/test/test_msgbpdft.py

    • Updates the reference energy for the ungrouped XMS-GB-PDFT test case following the solver changes.

Usage

mc = gbci.gbci(
    mf,
    ncas=2,
    nelecas=(1, 1),
    group_a={"atom": [0]},
).run()

grad = mc.nuc_grad_method().kernel()

Reference

Comment thread pyscf/gbci/direct_gbci.py Outdated
Comment thread pyscf/grad/test/test_gbci_grad.py
Comment thread pyscf/grad/test/test_gbci_grad.py Outdated
self.assertAlmostEqual(
float(grad[0, 2]), LIF_TRIPLET_ROHF_GRAD_Z, 9)
self.assert_response(
bath_response_norm, LIF_TRIPLET_ROHF_BATH_RESPONSE_NORM, places=9)

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are some of these tolerances too tight? On my laptop:

(pyscf) herme068@herme068:test$ pytest test_gbci_grad.py 
======================================================== test session starts ========================================================
platform linux -- Python 3.12.7, pytest-9.0.2, pluggy-1.5.0
rootdir: /home/herme068/gits/pyscf_ext/pyscf-forge
configfile: pyproject.toml
plugins: anyio-4.6.2
collected 4 items                                                                                                                   

test_gbci_grad.py .F..                                                                                                        [100%]

============================================================= FAILURES ==============================================================
______________________________________________ KnownValues.test_lif_triplet_rohf_4o4e _______________________________________________

self = <test_gbci_grad.KnownValues testMethod=test_lif_triplet_rohf_4o4e>

    def test_lif_triplet_rohf_4o4e(self):
        grad, bath_response_norm, rohf_response_fp = get_gbci_grad(
            f"Li 0 0 0; F 0 0 {BOND_LENGTH}", 4, (2, 2),
            spin=2, mf_cls=scf.ROHF)
        self.assertAlmostEqual(
            float(grad[0, 2]), LIF_TRIPLET_ROHF_GRAD_Z, 9)
>       self.assert_response(
            bath_response_norm, LIF_TRIPLET_ROHF_BATH_RESPONSE_NORM, places=9)

test_gbci_grad.py:155: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
test_gbci_grad.py:121: in assert_response
    self.assertAlmostEqual(value, ref, places)
E   AssertionError: 0.008977610258204424 != 0.008977611205020856 within 9 places (9.468164325482364e-10 difference)
====================================================== short test summary info ======================================================
FAILED test_gbci_grad.py::KnownValues::test_lif_triplet_rohf_4o4e - AssertionError: 0.008977610258204424 != 0.008977611205020856 within 9 places (9.468164325482364e-10 difference)

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

and it looks like the next assert after this (that my laptop didn't get to) consistently fails outright in the github actions, so there's clearly something wrong with this test

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you for your comments. Previously, I tested the intermediate Z-vectors against reference values obtained using lib.fp. However, I found that these values are sensitive to the orbital gauge, which can lead to spurious CI failures even when the solutions are physically equivalent. I therefore replaced these checks with comparisons of the Z-vector norms, which are more robust to orbital-gauge changes. This resolved the gauge-related CI failures.

@MatthewRHermes
MatthewRHermes merged commit 06a9a38 into pyscf:master Aug 25, 2026
6 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants