Skip to content

PySCFTheory: fix broken-symmetry SCF on GPU - #528

Merged
RagnarB83 merged 1 commit into
RagnarB83:masterfrom
Carlos-A-Ramos-G:fix-bs-scf-gpu
Oct 21, 2026
Merged

RagnarB83 merged 1 commit into
RagnarB83:masterfrom
Carlos-A-Ramos-G:fix-bs-scf-gpu

Conversation

@Carlos-A-Ramos-G

Copy link
Copy Markdown
Contributor

Two bugs in PySCFTheory.run_BS_SCF (BS=True) make the broken-symmetry procedure fail with platform="GPU". Neither appears on CPU, which is probably why they went unnoticed.

1. The spin change never reaches the SCF. run_BS_SCF sets self.mol.spin before the high-spin and the BS steps. On GPU, prepare_run does self.mf = self.mf.to_gpu(), which deep-copies the Mole. The SCF then uses self.mf.mol, which keeps the spin it was built with. So the "high-spin" step silently runs at the target (BS) spin, from scratch. In our [4Fe-4S] case (HSmult 19) it ran at Ms = 0 with ⟨S²⟩ = 0 and diverged. Fix: a small helper, set_BS_spin, that sets the spin on self.mol and, when it is a different object, on self.mf.mol.

2. The flipped density is passed as a Python list. After the α/β swap, dm = [dma, dmb] goes to mf.run(dm). On GPU, dma/dmb are cupy arrays, and gpu4pyscf calls cupy.asarray(dm0), which cannot build an array from a list of cupy arrays and raises AssertionError (cupy 14.2). Fix: pass one (2, nao, nao) array, cupy.stack([dma, dmb]) on GPU and np.array([dma, dmb]) on CPU.

Changes

  • ash/interfaces/interface_pyscf.py: set_BS_spin helper, used for both spin changes in run_BS_SCF; stacked initial density (+16/−3).
  • ash/tests/test_pyscf_broken_symmetry.py (new, CPU only, about 1 s): BS on Li2 stretched to 6 Å (UHF/def2-SVP, HSmult 3, flip atom 1). It checks the spin the SCF actually uses at each step (2, then 0), that the BS step gets a single array, and that ⟨S²⟩ ≈ 1. A second case copies the Mole into mf, the way to_gpu() does, so the GPU failure is covered without a GPU.

Testing

  • New tests: both fail on master (spins [0, 0] instead of [2, 0]; list passed to the SCF) and pass with this PR. test_pyscf_scf_convergence.py still passes.
  • Real GPU (gpu4pyscf 1.8.1, cupy 14.2, A100), same Li2 system through PySCFTheory(platform="GPU", BS=True, ...):
HS step: mf.mol.spin, ⟨S²⟩ BS step result
master 0, 0.000 AssertionError in cupy.asarray crash
this PR 2, 2.000 0, ⟨S²⟩ 0.989, ndarray passed E = −14.850294 Eh

Note for the test molecule: H2 can't be used on GPU. Its triplet has no β electrons, and gpu4pyscf's get_occ logging then fails (TypeError: must be real number, not NoneType). That's why the test uses Li2.

🤖 Generated with Claude Code

run_BS_SCF changed the spin on self.mol, but on GPU mf.to_gpu() deep-copies the
Mole, so the SCF (which uses self.mf.mol) never saw it: the "high-spin" step ran
at the initial spin. Set the spin on self.mf.mol as well (set_BS_spin).

The flipped density was passed as a list [dma, dmb]; gpu4pyscf's
cupy.asarray(dm0) cannot convert a list of cupy arrays (AssertionError).
Pass a single (2,nao,nao) array (cupy.stack on GPU, np.array on CPU).

Adds a CPU test of the BS procedure (Li2 at 6 A, UHF), including a case where
mf has its own copy of the Mole as after to_gpu().

Co-Authored-By: Claude Opus 5.5 <[email protected]>
@RagnarB83

Copy link
Copy Markdown
Owner

Good catch, indeed this was never tested on the GPU

@RagnarB83
RagnarB83 merged commit 2c7c056 into RagnarB83:master Oct 21, 2026
1 check 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