Skip to content

Relativistic 2e 3- and 4-center integrals#399

Draft
kshitij-05 wants to merge 33 commits into
masterfrom
kshitij/feature/2e_rkb_ints
Draft

Relativistic 2e 3- and 4-center integrals#399
kshitij-05 wants to merge 33 commits into
masterfrom
kshitij/feature/2e_rkb_ints

Conversation

@kshitij-05

@kshitij-05 kshitij-05 commented Feb 9, 2026

Copy link
Copy Markdown
Collaborator
  • Implement 2-electron 4-center relativistic integrals with restricted kinetic balance condition (RKB).

    • (LL|SS)
    • (SS|SS)
    • (LS|LS) [gaunt]
  • Implement 2e 3-center relativistic integrals with RKB.

    • (X|SS)
    • (X|LS)

    Also adds σpeμσp operator for small component dipole moment integrals

@loriab loriab left a comment

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.

I just noticed this and saw some tweaks to propose. You might want to add the new class to INSTALL.md, too.

Comment thread include/libint2/config.h.cmake.in Outdated
Comment thread include/libint2/config.h.cmake.in Outdated
Comment thread include/libint2/engine.impl.h Outdated
Comment thread include/libint2/engine.impl.h Outdated
Comment thread include/libint2/engine.impl.h Outdated
Comment thread include/libint2/engine.impl.h Outdated
Comment thread cmake/modules/int_am.cmake Outdated
message(VERBOSE "setting components ${_amlist}")

foreach(_cls ONEBODY;ERI;ERI3;ERI2;G12;G12DKH)
foreach(_cls ONEBODY;ERI;RKB_ERI;ERI3;ERI2;G12;G12DKH)

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.

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.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

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
…+ 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.
@kshitij-05 kshitij-05 force-pushed the kshitij/feature/2e_rkb_ints branch from 25265a6 to 41f1dad Compare March 22, 2026 18:19
@kshitij-05 kshitij-05 force-pushed the kshitij/feature/2e_rkb_ints branch from 025cdfb to 3194cad Compare April 1, 2026 16:26
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.
@kshitij-05 kshitij-05 force-pushed the kshitij/feature/2e_rkb_ints branch from 3194cad to 9b50b0b Compare April 10, 2026 18:02
…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.
@evaleev evaleev added this to the 2.14 milestone Apr 17, 2026
(μ ∂_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 ν.
@kshitij-05 kshitij-05 changed the title Relativistic 2e integrals Relativistic 2e 3- and 4-center integrals Jun 8, 2026
@kshitij-05 kshitij-05 requested a review from Copilot June 8, 2026 22:21

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

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 oprop operator, 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 DerivGaussV2 to enable better code sharing for derivative-Gaussian expansions.
  • Extend the runtime Engine to 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_indexquaternion_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.

Comment thread include/libint2/engine.impl.h Outdated
Comment thread include/libint2/engine.h Outdated
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.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 11 out of 29 changed files in this pull request and generated 1 comment.

Comment thread include/libint2/cxxapi.h
Comment on lines 36 to +40
#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"
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.

4 participants