From 1ce8fd8114b28c91c0ed93ff4cef4ef1530942b7 Mon Sep 17 00:00:00 2001 From: Thijs Vogels Date: Mon, 18 May 2026 13:34:23 +0000 Subject: [PATCH 1/2] Fix two retry_scf bugs that surface on gpu4pyscf The retry path in src/skala/pyscf/retry.py contained two gpu4pyscf-incompatibilities that together caused single-point calculations to fail on GPU but succeed on CPU for systems whose first SCF call did not converge (see msr-ai4science/feynman#21391): 1. SCFState.post_cycle_callback asserted isinstance(envs["norm_ddm"], (float, int)) but gpu4pyscf stores norm_ddm as a 0-d cupy.ndarray, so the assertion fires on the first GPU SCF cycle. Coerce to a Python float instead so downstream consumers see a uniform numeric type on both backends. 2. increment_level_shift did `if level_shift > 0`. CPU pyscf defaults SCF.level_shift to 0, gpu4pyscf defaults it to None, so the comparison raises TypeError on the GPU retry path. Treat None and 0 identically as "no level shift yet" and start from level_shift_init. Verified by reproducing the original failure on all 8 GPU-failing structures from feynman#21391 (CCoN, H2FeO2, H2Cl2Fe, CH3Cl, CH3Br, C3H3IN2, H10N2O2Pt, C2H6O2Pd) and confirming they now succeed with with_retry=True on cuda. Existing test_scf_retry.py still passes. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- src/skala/pyscf/retry.py | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/skala/pyscf/retry.py b/src/skala/pyscf/retry.py index da8d694..c135bc8 100644 --- a/src/skala/pyscf/retry.py +++ b/src/skala/pyscf/retry.py @@ -86,10 +86,10 @@ def post_cycle_callback(self, envs: dict[str, Any]) -> None: if "norm_ddm" not in envs: envs["norm_ddm"] = np.linalg.norm(envs["dm"] - envs["dm_last"]) - norm_ddm = envs["norm_ddm"] - assert isinstance(norm_ddm, (float, int)), ( - f"Expected norm_ddm to be a float, got {type(norm_ddm)}" - ) + # gpu4pyscf stores norm_ddm as a 0-d cupy.ndarray; CPU pyscf as a numpy + # scalar. Coerce to a Python float so downstream consumers (and the + # `dm_change_per_cycle` list) always see a uniform numeric type. + norm_ddm = float(envs["norm_ddm"]) self.dm_change_per_cycle.append(norm_ddm) if not isinstance(mo_energy, list) and len(mo_energy.shape) == 1: @@ -194,13 +194,13 @@ def retry_scf( def increment_level_shift( - level_shift: float, + level_shift: float | None, max_level_shift: float = 0.5, level_shift_init: float = 0.1, level_shift_increment: float = 0.2, ) -> float: - return ( - min(level_shift + level_shift_increment, max_level_shift) - if level_shift > 0 - else level_shift_init - ) + # gpu4pyscf SCF objects default ``level_shift`` to None, CPU pyscf uses 0. + # Treat both as "no level shift yet" and start from ``level_shift_init``. + if not level_shift: + return level_shift_init + return min(level_shift + level_shift_increment, max_level_shift) From 6edc356b4c3c6419e6e0155818e3e444fa794f50 Mon Sep 17 00:00:00 2001 From: Thijs Vogels Date: Mon, 18 May 2026 13:42:26 +0000 Subject: [PATCH 2/2] Bump version to 2026.5 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 98edcf1..74b8cbb 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "skala" -version = "2026.4" +version = "2026.5" description = "Skala Exchange Correlation Functional" authors = [] license-files = ["LICENSE.txt"]