Relativistic 2e 3- and 4-center integrals#399
Conversation
loriab
left a comment
There was a problem hiding this comment.
I just noticed this and saw some tweaks to propose. You might want to add the new class to INSTALL.md, too.
| message(VERBOSE "setting components ${_amlist}") | ||
|
|
||
| foreach(_cls ONEBODY;ERI;ERI3;ERI2;G12;G12DKH) | ||
| foreach(_cls ONEBODY;ERI;RKB_ERI;ERI3;ERI2;G12;G12DKH) |
There was a problem hiding this comment.
there's a slight disadv to the underscore if ppl are splitting the integral codes (e.g., rkb_eri_ffff_d1) on underscore, but I think RKB_ERI is fine.
There was a problem hiding this comment.
Will change the label token from e.g., rkb_eri_ffff_d1 to rkberi_ffff_d1, which should resolve any underscore-driven parsing
… unique am shell sets and phase change for this operator
…l differentiator when on MacOS
…t and more cleanup
…+ progress bar + sign fix - ShellQuartetSetPredicate: add braket-swap tiebreaker for bra_ket_coswappable operators (σpσpCoulombσpσp). When la+lb == lc+ld, use max(la,lb) <= lc to pick one canonical representative, reducing duplicate quartet generation. - Engine (engine.impl.h): update swap_braket logic for opop_coulomb_opop to match the new predicate tiebreaker. Add coupled-swap sign correction in the swap_braket branch (was missing — exposed by d-shell testing). - build_libint.cc: disable CSE (do_cse/condense_expr) for multi-component operators since their 16 components share no intermediates at the expression level. This eliminates the superlinear optimize_rr_out bottleneck (e.g., 8.8s → 71ms for (ss|ds) prerequisite DAG). - build_libint.cc: fix compilation when only LIBINT_INCLUDE_RKB_ERI is defined (without LIBINT_INCLUDE_ERI): extend #ifdef guards for build_TwoPRep_2b_2k, add forward declaration, move make_descr to detail namespace, use if constexpr for component descriptor construction. - buildtest.h: add CodeGenProgress spinner showing elapsed time, function count, and current task name on stderr during code generation. - int_am.cmake: fix typo in OPT_AM variable reference.
25265a6 to
41f1dad
Compare
025cdfb to
3194cad
Compare
Apply the same optimization as HRR: since differentiation of a Gaussian depends only on that shell's quanta (not spectators), generate code once per unique differentiated shell and pass spectator dims at the call site. Verified: 295,944 assertions pass (Coulomb + ERI derivs + RKB integrals).
handle_trivial_nodes() used default_dims() (hardcoded "1") before
adapt_dims_() provided correct runtime dims ("lowdim"/"highdim").
Pass localdims through optimize_rr_out → handle_trivial_nodes.
3194cad to
9b50b0b
Compare
…p files Add code-sharing overrides (generate_label, spfunction_call, adapt_dims_, generate_code) to CR_11_Coulombσpσp_11 and CR_11_σpσpCoulombσpσp_11. Shell quartets with the same quaternion component share a single function parameterized by highdim. Hand-emits element-wise loops to avoid TwoPRep particle-swap child deduplication issues in DAG-based codegen.
(μ ∂_a ν | 1/r12 | κ ∂_b λ) for a, b ∈ {x,y,z}. Needed for Gaunt LS
Fock where coulomb_opop's 4 folded outputs are rank-deficient. Only
2-fold bra↔ket symmetry (p1_p2_swappable, no within-side swap); uses
dedicated predicate la+lb <= lc+ld. Also: LIBINT2_SIMPLE_CORE_EVAL_CASE
macro to compactify Coulomb-family dispatch in engine.impl.h.
Scalar trace + 3 antisym + 5 sym-TL replaces raw 3*a+b dyadic. Bra<->ket swap: per-component sign flip on antisym (was: index transpose). p1_p2_swappable=true. Tests pass: 2,112,533 assertions.
12 components = 3 dipole directions × 4 Pauli quaternion (trace + 3 antisym), mirroring σpVσp's fold. Engine origin via set_params. Master integral list split via mpl::joint_view (boost list50 limit).
…rkb_ints # Conflicts: # export/tests/unit/test-1body.cc # include/libint2/engine.h # include/libint2/engine.impl.h # src/bin/libint/comp_1_σpVσp_1.h # src/bin/libint/oper.h
Rename the 1-body σ·p · r · σ·p ("σ·p-dressed dipole", SS-block dipole)
operator from oprop/σpRσp to σpeμσp for consistency with the σpXσp naming
of its sibling RKB operators and with the mpqc4 consumer name
(Operator::Type::σpeμσp). Mechanical rename across the compiler descriptor
(oper.h σpRσp_Descr → σpeμσp_Descr, label opRop[] → opemuop[]), the master
ints/rrs lists, strategy.cc specializations, build_libint.cc task tuples and
make_descr, the runtime enum (Operator::oprop → σpeμσp) and operator_traits,
the operator-list macro / compute_primdata in engine.impl.h, and the 1-body
unit test. ASCII codegen label is "opemuop"; runtime enum is σpeμσp.
New 2-body operator Coulombσp / Operator::coulomb_op: the 3-center single-σ·p "DF-Gaunt" B-factor (P | 1/r12 | μ, σ·p ν), with the fitting function P a spectator on the bra (BraKet::xs_xx) and a single σ·p acting on the 2nd ket AO function ν. Unlike Coulombσpσp (two σ·p on the ket pair, folded via the Dirac identity into 4 quaternion components), a single σ·p does no σ·σ folding: the result is the bare Cartesian gradient of ν — the SO(3) vector irrep with 3 components (x,y,z). Each component is exactly one first-derivative ERI child, routing through the TwoPRep DerivGaussV2 strategy. 3-center only (no 4-center sibling); feeds mpqc4 DF-Gaunt B_G. Wiring: - oper.h: Coulombσp_Descr (max_key=3, cartesian_index, label coulomb_op[X/Y/Z]). - comp_11_Coulombσp_11.h (new): CR_11_Coulombσp_11 — single deriv child d_k, element-wise-copy generate_code (0 flops), shared across shell combos. - master_ints_list.h / master_rrs_list.h / strategy.cc: register the int set, RR, and MasterStrategy. - build_libint.cc: RKB_ERI3 task tuples add 3coulomb_op; build_TwoPRep_1b_2k emits the FULL (lc,ld) square for CoulombσpOper (σ·p on ν only ⇒ μ↔ν is not a symmetry) and 3 operator components; TWOBODY_TASKOPER adds "coulomb_op" so LIBINT2_TASK_EXISTS_3coulomb_op is defined. - engine.h: enum coulomb_op + operator_traits (nopers=3, deriv_order=1). - engine.impl.h: operator-list entry, SIMPLE_CORE_EVAL_CASE, and a symmetry branch (swap_braket on xx_xs; no within-ket swap, no component remap). - test-2body.cc: "Coulombσp 3-center xs_xx" validates vs primitive eri() with a unit s-shell at bra-2 and a single first derivative on ν.
There was a problem hiding this comment.
Pull request overview
This PR extends Libint’s code generator and C++ engine to support relativistic restricted kinetic balance (RKB) 2-electron 4-center and 3-center integrals (including Gaunt LS bilinear forms), and adds the one-body (σ·p) r (σ·p) (“oprop”) operator. It also introduces an optimized “DerivGaussV2” recurrence strategy and improves generator progress reporting.
Changes:
- Add new operator descriptors/types for RKB Coulomb-family two-body operators and the one-body
opropoperator, wiring them through the generator’s master lists and strategy selection. - Update code generation graph/DAG plumbing to propagate correct implicit dimensions into RR optimization, and add
DerivGaussV2to enable better code sharing for derivative-Gaussian expansions. - Extend the runtime
Engineto recognize the new operators, adjust braket canonicalization/sign conventions, and add unit tests + CMake configuration switches for RKB ERIs (4c/3c).
Reviewed changes
Copilot reviewed 9 out of 25 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
| src/bin/libint/strategy.cc | Switch TwoPRep derivative RR strategy to DerivGaussV2; register new RKB operator master strategies. |
| src/bin/libint/rr.cc | Compute localdims earlier and pass dims into DAG optimization for correct trivial-node handling. |
| src/bin/libint/oper.h | Add descriptors for σpRσp, Coulombσpσp, σpσpCoulombσpσp, σpCoulombσp; rename quaternionic_index → quaternion_index for σpVσp. |
| src/bin/libint/master_rrs_list.h | Register new composite recurrence headers and typedefs, including DerivGaussV2. |
| src/bin/libint/master_ints_list.h | Add new integral-set types; split master integral list into two parts joined via joint_view. |
| src/bin/libint/gauss.cc | Make primitive labels filesystem-safe (case-insensitive FS) by adding an underscore marker for uncontracted. |
| src/bin/libint/dg.h | Thread ImplicitDimensions into optimize_rr_out() / handle_trivial_nodes(). |
| src/bin/libint/dg.cc | Implement dims-aware optimize_rr_out() and handle_trivial_nodes() behavior. |
| src/bin/libint/comp_deriv_gauss_v2.h | New optimized derivative-Gaussian recurrence relation with HRR-like code sharing. |
| src/bin/libint/comp_11_σpσpCoulombσpσp_11.h | New CR implementing 16-component (σ·p)(σ·p) (1/r12) (σ·p)(σ·p) decomposition and code emission. |
| src/bin/libint/comp_11_σpCoulombσp_11.h | New CR implementing 9-component SO(3) irrep decomposition for Gaunt LS “bilinear” operator. |
| src/bin/libint/comp_11_Coulombσpσp_11.h | New CR implementing 4-component quaternion decomposition for `(ab |
| src/bin/libint/comp_1_σpVσp_1.h | Align σpVσp descriptor API (quaternion_index) with naming changes. |
| src/bin/libint/comp_1_σpRσp_1.h | New CR implementing 12-component (σ·p) r_k (σ·p) fold. |
| src/bin/libint/buildtest.h | Add global codegen progress tracker and hook it into GenerateCode(). |
| src/bin/libint/build_libint.cc | Add RKB task wiring, component loops, canonicalization logic, and progress printing; template two-/three-center build helpers by operator. |
| include/libint2/engine.impl.h | Extend operator list, nargs=3 routing, braket canonicalization/sign rules, intrinsic deriv order handling, and core-eval dispatch for new operators. |
| include/libint2/engine.h | Add new Operator enum entries + traits (nopers, intrinsic_deriv_order) for RKB ops and oprop. |
| include/libint2/cxxapi.h | Allow building C++ API when only RKB ERIs are enabled as the 2-body class. |
| include/libint2/config.h.cmake.in | Add config macros for enabling RKB ERI and RKB ERI3 and their AM/OPT settings. |
| include/libint2.h | Update generated macro names (underscore normalization). |
| export/tests/unit/test-2body.cc | Add unit tests validating RKB Coulomb-family operators + 3-center paths and xx_xs alias behavior. |
| export/tests/unit/test-1body.cc | Add unit test validating oprop Hermiticity/sign conventions across bra↔ket. |
| CMakeLists.txt | Add CMake options for enabling RKB ERI / RKB ERI3 and their AM/optimization controls. |
| cmake/modules/int_am.cmake | Teach component generation to include RKB ERI and RKB ERI3 component sets; fix OPT_AM variable assignment. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
The (ss|ss) fast-path predicate in engine.impl.h used a single bitwise & between the deriv_order_ and intrinsic_deriv_order() boolean tests, which drops short-circuit evaluation. Use &&. (Copilot review.)
…lomb_op doc - INSTALL.md: document LIBINT2_ENABLE_RKB_ERI / RKB_ERI3 and their MAX_AM/OPT_AM options (Lori review). - CMakeLists.txt + config.h.cmake.in: state that RKB_ERI3/ERI3 MAX_AM set the density-fitting (auxiliary) basis AM on the single fitting center. - int_am.cmake: collapse the RKB ERI component-code token rkb_eri_ -> rkberi_ so the operator token is single, underscore-free (like eri/onebody/g12), avoiding ambiguity if codes are split on '_'. Public LIBINT2_*_RKB_ERI* options and the RKB_ERI class id are unchanged (Lori review). - engine.h: correct the Operator::op_coulomb_op docstring to describe the actual SO(3) irreducible 9-component layout, not a raw 3*a+b Cartesian dyadic (Copilot review).
operator_traits<op_coulomb_op> carried the same stale "raw 3*a+b Cartesian dyadic" description that the Operator enum doc had; correct it to the actual SO(3) irreducible 9-component layout (Scalar / Antisym / SymTL). Completes the Copilot docstring fix, which had only updated the enum copy.
- engine.h: default_braket(coulomb_op) now returns BraKet::xs_xx. coulomb_op has no 4-center kernel, so the rank-based xx_xx default silently dispatched to the plain-Coulomb `default` task and returned wrong integrals with no error in release builds. (Review finding #1.) - comp_11_σpσpCoulombσpσp_11.h / comp_11_Coulombσpσp_11.h: add a `default` branch to generate_code()'s child-count switch that throws instead of emitting a wrong/empty `target[hsi] = ...` expression if num_children() is ever an unexpected value (e.g. future child dedup). (Review finding #4.)
…, coulomb_op default-braket test - deriv_map.h (#2): DerivMapGenerator::instance() now throws a descriptive std::logic_error for unsupported (braket, deriv>0) combinations (e.g. xx_xs) instead of std::abort()-ing the host process. xx_xs derivatives remain unimplemented, but now fail catchably rather than crashing consumers. - comp_11_σpσpCoulombσpσp_11.h / comp_11_Coulombσpσp_11.h (#6): replace the guessed `max_nchildren = 100 // TODO` with the true bounds (9 and 3, derived from the densest fold component). This is a children_.reserve() hint only (generic_rr.h), so no behavior change. - test-2body.cc (#1): regression test that default_braket(coulomb_op)==xs_xx and that a coulomb_op engine built WITHOUT an explicit set() produces results identical to the explicit-xs_xx engine (guards the silent-wrong-integrals footgun fixed in the previous commit). Verified: low-AM (MAX_AM=2) compiler->export->library->test all green; RKB Coulomb case 7600->7653 assertions, full suite 2,114,807 / 33 cases, no regressions.
The four σ·p "element-wise fold" recurrence relations (Coulombσp, Coulombσpσp,
σpCoulombσp, σpσpCoulombσpσp) had byte-identical spfunction_call, adapt_dims_,
and generate_code scaffold logic copy-pasted four times; only the operator label
and the per-target RHS expression differ.
Extract the shared logic into three free-function helpers in the new
src/bin/libint/rkb_fold_codegen.h:
- rkb_fold_adapt_dims (the "highdim" dimension swap)
- rkb_fold_spfunction_call (call-site + total_dim emission)
- rkb_fold_generate_code (symbol assignment + function scaffold + the
target[hsi] = <rhs> loop)
Each comp header now delegates spfunction_call/adapt_dims_ in one line and keeps
only its operator-specific RHS computation (copy / nc-keyed / component-keyed)
before calling rkb_fold_generate_code. No CRTP/Instance()/inheritance changes —
the classes keep their exact GenericRecurrenceRelation base; the helpers are
chosen specifically to avoid touching the registry/hierarchy. This relies only
on the public RecurrenceRelation accessors (rr_target/rr_child/num_children/
label), passing the typed target_ where bra()/ket() iteration is needed.
Verified byte-identical: low-AM compiler->export->library->test reproduces the
exact pre-refactor counts (RKB Coulomb 7653 assertions, full suite 2,114,807 /
33 cases), so the generated code is unchanged.
| #if !defined(LIBINT_INCLUDE_ONEBODY) || \ | ||
| !(defined(LIBINT_INCLUDE_ERI) || defined(LIBINT_INCLUDE_ERI3) || \ | ||
| defined(LIBINT_INCLUDE_ERI2)) | ||
| defined(LIBINT_INCLUDE_ERI2) || defined(LIBINT_INCLUDE_RKB_ERI)) | ||
| #error \ | ||
| "C++ API is only supported if both 1-body and some (eri, eri3, eri2) 2-body integrals are enabled" | ||
| "C++ API is only supported if both 1-body and some (eri, eri3, eri2, rkb_eri) 2-body integrals are enabled" |
Implement 2-electron 4-center relativistic integrals with restricted kinetic balance condition (RKB).
Implement 2e 3-center relativistic integrals with RKB.
Also adds
σpeμσpoperator for small component dipole moment integrals