Repository navigation
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.
… 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
generate_label() returned "CR_σpCoulombσp_<n>", so the Unicode σ leaked into generated function names and source filenames (CR_σpCoulombσp_0.cc etc.), unlike the sibling fold CRs (CR_Coulombopop_, CR_opopCoulombopop_, CR_Coulombop_). Use "CR_opCoulombop_". generate_code() already selected the right-hand side from the component index but did not check num_children(); add the same guard as the other two fold CRs so a child collision in add_child() fails the export instead of producing a call with fewer src arguments than the expression uses.
…ument the RKB component conventions
Engine: operator_traits<Operator> gains pair_swap (independent/coupled/none),
braket_swap, pair_swap_phase(c), braket_swap_phase(c) and
braket_swap_remap(c). Engine::compute2 canonicalizes shell sets and applies
per-component phases and remaps from these traits; the per-operator branches
for coulomb_opop, coulomb_op, opop_coulomb_opop and op_coulomb_op are gone.
Engine::_initialize no longer leaves stale target/scratch buffers when the
library lacks the kernels.
Compiler: the 2-body operator descriptors gain the matching p1p2_swappable and
pair_swap traits, ShellQuartetSetPredicate takes PairSwap instead of a bool,
and build_TwoPRep_{2b_2k,1b_2k} derive the shell sets and the per-component
targets (make_descr over max_key) from the descriptor instead of
std::is_same branches. Adds the missing make_descr specializations for
σpCoulombσp and Coulombσp. Generated shell-set coverage is unchanged
(verified per task against the predicates).
engine.h gains a doc page (\page rkb_operators) with one table of the
component layouts, spin folding and naming (engine vs compiler vs task label)
of the four RKB 2-body operators and opemuop.
Also: the progress counter no longer counts each 2-body kernel twice, and
print_config reports RKB 3-center ERIs (and spells "kinetic balance").
- Libint compiler requires C++17 (it uses if constexpr); was compiled as a C++14 extension on clang and relied on the gcc default elsewhere - code generation progress: on a terminal keep the single updating line; in logs (CI) print one line per kernel so a failure can be attributed - the Hartree-Fock examples are only built/tested if the library has 4-center Coulomb integrals, so the check target passes for an RKB-only library - config.h: fix copy-pasted comments of the RKB options - CHANGES: document the new std::invalid_argument for cartesian fitting shells with LIBINT2_ERI3_PURE_SH and the compiler's C++17 requirement
…ject xx_xs derivatives early; defined AM-limit state - The std::invalid_argument for a cartesian fitting shell with LIBINT2_ERI3_PURE_SH (a287bd7) now applies to derivative order 0 only: the generator marks the fitting shell pure_sh only for deriv_level == 0 (build_libint.cc), so derivative kernels are cartesian-complete and were wrongly rejected. Same condition on the ERI2 asserts (xs_xs), whose ket message also said "in bra". - BraKet::xx_xs with deriv_order > 0 is rejected at initialize(); before it reached a std::logic_error from DerivMapGenerator::instance inside compute2. - _initialize() clears buildfnptrs_ before throwing lmax_exceeded, so an Engine that outlives the exception throws missing_kernel instead of dispatching a build table that does not match primdata_. hard_lmax_ and hard_default_lmax_ are initialized; they were indeterminate for a kernel-less or default-constructed Engine and copied as such.
…asses in the derivative glob Upstream e22dfdd renamed set(${class}_MAX_AM ...) to LIBINT_${class}_MAX_AM but left the reads in the scalar branch of both process_integrals_class* macros bare, so with a scalar LIBINT2_<class>_MAX_AM the _candidate_<class>_d<d> values were empty: Libint2_ERI_COMPONENTS under-reported the generated kernels (find_package(Libint2 COMPONENTS ...)), the "Enabling integrals class X to max AM" message printed a blank, and the AM-range validation was dead. The restored validation rejects negative values (the list branch's rule) rather than zero. Also: LIBINT2_ENABLE_RKB_ERI/RKB_ERI3 take part in _glob_classes_derivs, and _rkb_eri3_impure_sh is renamed _rkb_eri3_components now that LIBINT2_RKB_ERI3_PURE_SH is gone.
…RI3_ORDER Mirrors the RKB_ERI feature macros so a consumer can test for 3-center RKB support without probing LIBINT2_TASK_EXISTS_3coulomb_opop.
…function label directional() is always true, so a shell carrying derivative quanta in two directions (second derivatives) is a valid target for two instances whose children and a[dir] coefficients differ. The RRStack deduplicates generated functions by label, which omitted dir, so only the first instance's function was emitted and correctness relied on the tactic tie-break selecting that same instance. Encode dir in both the code-shared and the translational-invariance labels (the latter inherited the omission from CR_DerivGauss). Also drop a dead `if (np == 1) return true;` in expl_low_dim().
|
Audit of the branch before pushing: two review passes (branch vs Fixed in this push
Already covered by f3d21c1 / 884e8d7: per-operator swap branches (now traits), progress double count and the stderr spinner, Verified: light build, all max AM 2, ONEBODY/RKB_ERI/RKB_ERI3 at derivative 0 and ERI/ERI3 at derivative 1: compiler tests pass, export passes, the configure log now prints the max AM values; the library build and Update (build finished): on the pushed tree ( |
…g components `targets_` is a std::vector with ext_stack_allocator, its elements live in primdata_[0].targets. The component remap after a bra-ket swap (f3d21c1) copied that vector (`const auto tmp = targets_;`); the copy inherits the allocator, and whether the copy's storage overlaps the original's depends on how the standard library maintains the bump pointer, so on libc++ (mingw CLANG64 CI lane) the remap read entries it had already overwritten and the opop_coulomb_opop components came out scrambled for every quartet that needs a bra-ket swap ((s d|s p) in "RKB Coulomb integrals"; the canonical kernel (p s|d s) was bit-identical to Linux). Copy the pointers into a local std::array, as the pre-f3d21c16 code did. Also make ext_stack_allocator safe to copy through a container: select_on_container_copy_construction() returns a heap-only allocator, and allocate()/deallocate() handle the heap-only state.
|
Windows CI, two separate failures, both bisected by job history to commits of today (neither from the audit fixes):
|
…i parity for negative arguments SolidHarmonicsCoefficients::coeff() called an anonymous-namespace `parity` unqualified. Under MSVC-style delayed template parsing (clang-cl's default) an unqualified name in a template body is bound at instantiation, so in a TU that had brought another `parity` into namespace libint2 the call resolved to that one instead. test_eri/eri.h declares such a libint2::parity, and it returns +1 for negative odd arguments (`a % 2 == 1` is false for -1). Since a287bd7 test-2body.cc (which includes test_eri/eri.h) instantiates the solid harmonics transforms, its copy of coeff<double> won the COMDAT for the whole unit-test executable, and the one coefficient that depends on parity(-1) for l <= 2, the xx entry of the d-shell m=0 row, came out +0.5 instead of -0.5. Every 1-body absolute-reference test with pure shells then failed on the windows-clang-cl CI lane while all other lanes, which use two-phase lookup, passed. Reproduced with clang++ -fdelayed-template-parsing. - solidharmonics.h: move parity into libint2::solidharmonics::detail, make it constexpr and correct for negative arguments, call it qualified - test_eri/eri.h: libint2::parity returns (-1)^a for negative a too - test-2body.cc: check coeff(2,0,2,0,0) == -0.5 from the TU that includes test_eri/eri.h
|
clang-cl root cause found and fixed in 307d3dd. |
…ment xx_xs, port generic code to DerivGaussV2, extend tests - build_libint: LIBINT_RKB_ERI[3]_OPT_AM were no-ops because CSE/condense_expr were forced off for multi-component operators; both builders now treat them as every other class (CSE only affects unrolled expressions, the DAG dedups by instance) - engine.h: document that with BraKet::xx_xs sigma.p acts on the user's bra for coulomb_opop/coulomb_op; restore the rkb_operators table (clang-format had reflowed it) and guard it with clang-format off/on - DerivGaussV2: implement has_generic/generic_header/generic_instance so LIBINT2_ENABLE_GENERIC_CODE keeps working for derivative ERIs; the generic call takes the shared function's runtime highdim/lowdim - CHANGES: list the library-wide changes (xx_xs energies, DerivGaussV2 for all ERI derivative kernels, missing_kernel instead of asserts, int_am.cmake validation) - CI: the macOS Release lane also builds 2nd ERI derivatives - tests: opemuop checked vs finite differences for all s/p/d pairs (contracted, pure, displaced origin); new test that a failed re-initialization leaves the Engine throwing missing_kernel
Resolves the conflict in CHANGES.
…le in buildtest.h GenerateCode() in buildtest.h references g_progress, which was defined only in build_libint.cc and test.cc, so libint-compiler-eri-test-generate failed to link.
…et; fix the RKB xx_xs alias test - set() changes oper_/braket_ before calling initialize(), so when initialize() rejected RKB or xx_xs derivatives the Engine kept the kernel table of the previous operator and compute() dispatched it (e.g. eri1 kernels for coulomb_opop). Clear buildfnptrs_ before throwing, as for lmax_exceeded, so compute() throws missing_kernel; add a test. - the xx_xs alias test constructed its Engines with the default (4-center) braket, which throws lmax_exceeded when LIBINT2_RKB_ERI_MAX_AM is below the 3-center AM; pass the braket to the constructor.
|
@kshitij-05 I merged master twice (#445/#446, then #449) and ran another adversarial review of the branch. Nothing blocks the merge. Three bugs are fixed in the commits I just pushed. Three warnings are left for you, because they depend on conventions you chose. The two items from the previous round are fixed:
Fixed
Warnings: please take a look
Notes
This review was agent-generated and human-reviewed. |
- "lmax_exceeded leaves no kernels" aborted in Debug builds: set(BraKet::xx_xx) changes the braket before initialize() throws, so the Engine is left as a kernel-less 4-center Engine and the 3-shell compute(fit, s, s) that followed tripped the argument-count assert of compute() (Release builds fell through to missing_kernel by accident). Check the braket instead, and add the coulomb_opop/3coulomb_opop variant of the test, which is live whenever LIBINT2_RKB_ERI3_MAX_AM > LIBINT2_RKB_ERI_MAX_AM. - The Release lane with LIBINT2_MAX_AM="2;2;1" and ERI derivatives up to 2 (introduced in aceb113) segfaulted in the Fortran ERI test and example and failed hartree-fock++: all three computed 2nd-derivative integrals over a d shell although the 2nd-derivative kernels stop at p. The Fortran test and example clamp the shells of each derivative order to LIBINT2_MAX_AM_eri<d>; hartree-fock++ computes the 2-body forces/hessian (and the derivative 2-center checks) only if the kernels support the basis and says so otherwise, which the validator accepts. - The xx_xs alias and coulomb_op 3-center tests limit the AO shells by LIBINT2_MAX_AM, not by the fitting-center limit.
…e evaluator in set(BraKet)/set_max_l; document the σ·p prefactors compute2() checked the angular momentum of each shell against the limit of its center only by assert. initialize() checks lmax_ against the most permissive center, so with a center-dependent table (3eri, or 3coulomb_opop with LIBINT2_RKB_ERI3_MAX_AM != LIBINT2_MAX_AM) an AO shell could pass that check and, in Release, index the kernel table out of range and run another shell set's kernel. Throw lmax_exceeded instead, in all builds; the asserts also used <= where < was needed (hard_lmax_ is the limit + 1). The task label is kept for the message. set(BraKet) and set_max_l() did not rebuild core_eval_pack_, whose order is braket_rank() * lmax_ + deriv_order_, so e.g. Engine(..., BraKet::xs_xx) followed by set(BraKet::xx_xx) could need a higher Boys order than the evaluator was built for. The rkb_operators page gets a prefactor column: all kernels differentiate with respect to the centers of the functions (∂_X = -∂_r), so σ·p contributes +i (on b, d) or -i (on the conjugated a, c). coulomb_opop and opop_coulomb_opop need no prefactor beyond the folded spin block q₀ + i σ·q, coulomb_op needs +i, op_coulomb_op needs -1. The old comment on coulomb_op said -i, which is wrong. The page is moved so that it no longer separates the doc of rank() from the function, and the sentence "acts on b and/or d" (wrong for coulomb_opop and opop_coulomb_opop) is replaced by a column.
… children and the generated code Each of the four σ·p fold relations (CR_11_Coulombσp_11, CR_11_Coulombσpσp_11, CR_11_σpCoulombσp_11, CR_11_σpσpCoulombσpσp_11) kept its expansion twice: the order of the make_child() calls in the constructor and the sign string in generate_code(). The child-count guard caught a deduplicated child but not a reordering. Now each relation defines fold_terms(component): a list of (coefficient, derivative direction on a, b, c, d) terms, from which rkb_fold_make_children() creates the children (and the symbolic expression) and rkb_fold_generate_code_from_terms() emits "target = Σ coef_t src_t", so the two cannot disagree. The Pauli components of (σ·∇)(σ·∇') live in one table (rkb_fold_pair_terms); the 16-component relation is its product (with the -1 of i² on the cross × cross block), the 9 irreps of op_coulomb_op are listed explicitly. The generated kernels are unchanged up to the order of the terms in the dot × cross components and the spacing of a leading sign. Also: - σpσpCoulombσpσp_Descr::quaternion_index() is renamed component_index(): the 16 components are not a quaternion - the RKB descriptors note that their Multiplicative*_Props make hermitian() return +1, so HRR must stay out of their strategies - Coulombσp_Descr and CR_11_Coulombσp_11 state the correct prefactor (+i, not -i) and that the derivative is with respect to the center of the function - DerivGaussV2: the expansion formula is d/dA, not d/dr; the translational invariance instances no longer share the "CR_DerivGauss" label prefix with CR_DerivGauss's own instances (the RRStack deduplicates by label); the explicit-vs-generic decision uses the OPT_AM of the current task, not of the default task
|
Follow-up to the review of 2026-10-25, in 40e9389 (tests), 9fba2b0 (engine) and 046a313 (compiler). CI of 5a3755c: two failures, both in tests
Warnings
Notes
Verification (M2, Apple clang 17, gfortran 14)
|
Follow-ups to the reviews of #399: lmax checks, hartree-fock++ hessian coverage, compiler cleanups
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