Skip to content

Implement the 13 integrals missing relative to libcint 6.1.3 - #24

Open
susilehtola wants to merge 2 commits into
sunqm:masterfrom
susilehtola:missing-integrals
Open

Implement the 13 integrals missing relative to libcint 6.1.3#24
susilehtola wants to merge 2 commits into
sunqm:masterfrom
susilehtola:missing-integrals

Conversation

@susilehtola

@susilehtola susilehtola commented Aug 1, 2026

Copy link
Copy Markdown
Contributor

As I reported in October 2025 in pyscf/pyscf#3021, some PySCF tests failed with missing symbols. This turned out to be due to a mismatch in qcint and libcint: the former was missing some integral classes. This PR implements those missing integral types.

AI summary

qcint 6.1.3 does not export 13 integrals that libcint 6.1.3 provides. Five of them — int1e_r{2,4}_origi_ip2 and int3c1e_ip1_r{2,4,6}_origk — are required by PySCF >= 2.13 for nuclear gradients with GTH pseudopotentials, so PySCF built against qcint aborts with undefined-symbol AttributeErrors in pyscf.gto.pp_int / pyscf.pbc.gto.pseudo.pp_int. This was found while enabling the PySCF test suite in Fedora, where qcint provides libcint on x86-64; 10 tests fail.

Generated code (via scripts/gen-code.cl, which reproduces all previously committed autocode byte-for-byte):

  • src/autocode/deriv4.c (new): int1e_ipiprinvipip, int1e_ipipiprinvip, int1e_ipipipiprinv — already declared in auto_intor.cl, but the file had never been generated;
  • src/autocode/lresc.c (new): int1e_iprinvr, int1e_iprinviprip, int1e_rinvipiprip, int1e_ipiprinvrip;
  • src/autocode/int3c1e.c: int3c1e_ipip1, int3c1e_ip1ip2, int3c1e_ipvip1, int3c1e_ipip2;
  • src/cint1e_a.c / src/cint3c1e_a.c: the five PySCF-critical integrals listed above.

Hand-ported to the SIMD framework: int4c1e (src/g4c1e.c + src/cint4c1e.c, behind the same WITH_4C1E option as libcint). CINTg4c1e_ovlp vectorizes libcint's algorithm with one primitive quartet per SIMD lane, reusing the existing CINTg0_{lj,kj,il,ik}_4d expansion routines; the primitive loop follows CINT3c1e_loop_nopt extended to four shells.

A separate first commit adds src/autocode/deriv3.c to CMakeLists.txt: the file was in the tree and its integrals declared in cint_funcs.h, but it was never compiled, so int1e_ipipipnuc, int1e_ipipiprinv, int1e_ipipnucip, and int1e_ipiprinvip were also absent from the built library. include/cint_funcs.h is updated to declare the same integral set as libcint.

Validation. Every new integral was compared against libcint 6.1.3 over all shell tuples of a mixed s/p/d/f contracted/primitive basis, in both _sph and _cart forms: agreement is at machine precision (<= 3e-13; int4c1e at 1e-13). A smoke test of 17 pre-existing integrals shows no regressions. int3c1e_ip1_r6_origk was additionally validated against finite differences of int3c1e_r6_origk, because libcint's own hand-written version of that integral has a bug in its y component (an uninitialized g76 read; fix submitted separately as sunqm/libcint#132).

🤖 Generated with Claude Code

https://claude.ai/code/session_01TTc5De7stABc2q58NHV22E

susilehtola and others added 2 commits August 1, 2026 15:09
The file has been present in the source tree and its integrals
(int1e_ipipipnuc, int1e_ipipiprinv, int1e_ipipnucip, int1e_ipiprinvip)
are declared in include/cint_funcs.h, but it was never listed in
CMakeLists.txt, so the symbols were missing from the built library.
libcint compiles the same file.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TTc5De7stABc2q58NHV22E
qcint 6.1.3 lacked 13 integrals that libcint 6.1.3 exports. Five of
them (int1e_r{2,4}_origi_ip2, int3c1e_ip1_r{2,4,6}_origk) are required
by PySCF >= 2.13 for nuclear gradients with GTH pseudopotentials, so
PySCF built against qcint failed with undefined symbols
(pyscf/gto/pp_int.py, pyscf/pbc/gto/pseudo/pp_int.py).

Generated with scripts/gen-code.cl (which reproduces all previously
committed autocode byte-for-byte):
* src/autocode/deriv4.c (new): int1e_ipiprinvipip, int1e_ipipiprinvip,
  int1e_ipipipiprinv. The declarations were already present in
  scripts/auto_intor.cl but the file had never been generated.
* src/autocode/lresc.c (new): int1e_iprinvr, int1e_iprinviprip,
  int1e_rinvipiprip, int1e_ipiprinvrip; declarations added to
  scripts/auto_intor.cl following libcint.
* src/autocode/int3c1e.c: second derivatives int3c1e_ipip1,
  int3c1e_ip1ip2, int3c1e_ipvip1, int3c1e_ipip2.
* src/cint1e_a.c: int1e_r2_origi_ip2, int1e_r4_origi_ip2.
* src/cint3c1e_a.c: int3c1e_ip1_r2_origk, int3c1e_ip1_r4_origk,
  int3c1e_ip1_r6_origk.

Ported by hand to the SIMD framework:
* src/g4c1e.c, src/cint4c1e.c (new): int4c1e, enabled with the same
  WITH_4C1E cmake option as libcint. CINTg4c1e_ovlp vectorizes the
  libcint algorithm with one primitive quartet per SIMD lane, reusing
  the existing CINTg0_{lj,kj,il,lj}_4d expansion routines; the
  primitive loop follows CINT3c1e_loop_nopt extended to four shells.

include/cint_funcs.h is updated to declare the same set of integrals
as libcint (this also adds the previously undeclared
int1e_grids_ipip, which was already implemented).

All new integrals were validated numerically against libcint 6.1.3
over every shell tuple of a mixed s/p/d/f contracted/primitive test
basis, in both _sph and _cart forms; agreement is at machine
precision. int3c1e_ip1_r6_origk was additionally validated against
finite differences of int3c1e_r6_origk because libcint's own
implementation of that integral has a bug in its y component (it
reads the uninitialized work array g76).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TTc5De7stABc2q58NHV22E
@susilehtola

Copy link
Copy Markdown
Contributor Author

CI note: qcint's workflow downloads the test scripts from libcint master at run time (curl -O https://raw.githubusercontent.com/sunqm/libcint/master/testsuite/...). libcint master's test_int3c1e.py currently crashes with an undefined-symbol AttributeError on any pyscf release, because it compares the int3c1e_ipip1/ip1ip2/ipvip1/ipip2 integrals (added post-6.1.3 by libcint PR #129) against the installed pyscf's libcgto.so, which does not provide them. sunqm/libcint#132 fixes the test to skip integrals the installed pyscf lacks and adds self-contained finite-difference checks for int3c1e_ip1_r{2,4,6}_origk; once it lands, this PR's CI should be able to pass.

Also note that pyscf cannot serve as a numerical reference for int3c1e_ip1_r6_origk: released libcint <= 6.1.3 computes its y component from an uninitialized buffer (see sunqm/libcint#132), which is why that integral was validated here against finite differences of int3c1e_r6_origk instead.

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.

1 participant