Skip to content

Commit 5b711c8

Browse files
committed
deploy: 2ea3058
1 parent a55d5d0 commit 5b711c8

74 files changed

Lines changed: 373 additions & 65 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.
38.9 KB
Binary file not shown.
72.5 KB
Binary file not shown.

.doctrees/adr/index.doctree

85 Bytes
Binary file not shown.

.doctrees/environment.pickle

9.23 KB
Binary file not shown.
0 Bytes
Binary file not shown.

AI_CODING_GUIDE.html

Lines changed: 1 addition & 1 deletion
Large diffs are not rendered by default.

CITATIONS.html

Lines changed: 1 addition & 1 deletion
Large diffs are not rendered by default.

CONTRIBUTING.html

Lines changed: 1 addition & 1 deletion
Large diffs are not rendered by default.

DevelopingTeam.html

Lines changed: 1 addition & 1 deletion
Large diffs are not rendered by default.
Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
# ADR 0004: Two-Center-Factorized Three-Center Hamiltonian Terms
2+
3+
## Status
4+
5+
Accepted
6+
7+
## Context
8+
9+
The fixed (non-SCF) LCAO Hamiltonian contains two-center and three-center contributions. The
10+
kinetic term is two-center; the neutral-atom/local potential and nonlocal-pseudopotential parts
11+
generate three-center contributions when the potential/projector center `C` differs from the
12+
orbital centers `A` and `B`. DeePTB's existing two-body SK path parameterizes only two-center
13+
reduced matrix elements and expands them with a CG basis + `wigner_D` rotation
14+
(`dptb/nn/hamiltonian.py`). Message-passing embeddings (`Slem`/`Lem`/`Trinity`) capture many-body
15+
effects, but they mix `(Σ_C P_AC)(Σ_{C'} P_{BC'})` through node features and do not represent a
16+
genuine, shared-center three-center object.
17+
18+
We want an explicit three-center term that (a) stays inside DeePTB's "parameterize reduced values,
19+
never integrate" philosophy (ADR 0001) and (b) reuses the existing CG/rotation machinery and
20+
`OrbitalMapper` indexing (ADR 0002).
21+
22+
## Decision
23+
24+
Represent the three-center block by a **two-center factorization**:
25+
26+
H^(3)_{AB} = Σ_{C ≠ A,B} P_{AC} · D_C · P_{CB}, P_{CB} = P_{BC}^†
27+
28+
where `P_{AC}^{μα} = <φ_{μA}|β_{Cα}>` is a two-center orbital↔projector "integral value" and `D_C`
29+
is a small center-local, species-`C` coupling matrix. Consistent with ADR 0001, **the network emits
30+
the reduced (direction-independent) values of `P_{AC}` and the matrix `D_C` directly** — there are
31+
no orbitals on `C` and nothing is integrated. `P_{AC}` is placed into the local A–C frame by the
32+
same SK selection matrices and rotated to the global frame with `wigner_D`; `D_C` is block-diagonal
33+
in `l` (identity in `m`), hence rotation invariant.
34+
35+
The term is **additive** in block form, exactly like Trinity's existing two-body SK contribution
36+
(`deeptb.py`: `EDGE_FEATURES += EDGE_ATTRS`): `H^0_{AB} = H^(2)_{AB} + H^(3)_{AB}`.
37+
38+
Two cutoffs are used, matching the physics and DeePTB's existing neighbour lists: the target blocks
39+
`H^(3)_{AB}` are produced **only for the basis-cutoff bonds** (`EDGE_INDEX`, the user's `r_max`), so
40+
they occupy exactly the same block set as `<A|B>`; the projector reach `A-C`/`C-B` uses the separate,
41+
user-tunable **environment cutoff** (`ENV_INDEX` at `er_max`, built by
42+
`AtomicData.from_points(er_max=...)`). `er_max` is a knob alongside the projector basis size and
43+
`l_max`.
44+
45+
Implementation lives in `dptb/nn/threecenter.py` (`ThreeCenterFactorized`, `CenterCouplingMatrix`);
46+
it consumes the graph already in the `AtomicDataDict` (edge list, edge vectors) and uses
47+
`OrbitalMapper` for all orbital and **projector** indexing (the projector set is simply another
48+
`OrbitalMapper` basis), reusing `wigner_D`, `BesselBasis`, `ScalarMLPFunction`, `polynomial_cutoff`
49+
and `scatter` rather than re-implementing them.
50+
51+
Both the **hopping** correction `H^(3)_{AB}` (basis-bond edge blocks) and the **onsite** correction
52+
`H^(3)_{AA} = sum_{C != A} P_{AC} D_C P_{CA}` (node blocks) are assembled; the onsite term is the
53+
`A==B` "triangle", i.e. each env edge `C->A` on its own, scattered to atom `A`.
54+
55+
It is a **Trinity-only** feature, wired **entirely inside `dptb/nn/embedding/trinity.py`** (plus its
56+
config schema in `dptb/utils/argcheck.py`); `dptb/nn/deeptb.py` and the rest of the pipeline are
57+
untouched. An optional `three_center` block **inside the trinity embedding config** builds the module
58+
in `Trinity.__init__`, and `Trinity.forward` assembles the correction and adds it to the reduced
59+
`EDGE_FEATURES`/`NODE_FEATURES`. Because Trinity emits *reduced-equivariant* features (the block
60+
transform happens later in `NNENV`), the correction is added via `to_reduced` — the CG decomposition
61+
that is the exact inverse of `E3Hamiltonian(decompose=False)` — so the existing `NNENV` transform then
62+
turns (many-body + three-center) into blocks together, with the two-body `EDGE_ATTRS` added on top as
63+
before. The config lives under the trinity variant, so `three_center` on any other embedding is an
64+
argcheck error.
65+
66+
A single trinity `mode` (Trinity-only; replaces the old `only2b`/`exclusive`) selects the channels on
67+
top of the always-present two-body base: `"2b"` = 2b only; `"3b"`/`"2b+3b"` = add the three-body term;
68+
`"full"` = add three-body + env (message passing). Following the original convention the 2b base is
69+
always produced by NNENV, so `deeptb.py` is untouched; the 3b/2b+3b/full modes require a `three_center`
70+
block, and the projector reach needs the environment neighbour list, so `er_max` must be set. The
71+
`three_center` module (hence its parameters) is built whenever the block is configured, *independent of*
72+
`mode`, so the `state_dict` is identical across modes and one can warm-start progressively.
73+
74+
Trainability is controlled by a separate, orthogonal `freeze` list (any subset of `["2b","3b","env"]`)
75+
that sets `requires_grad=False` on the two-body term, the three-center term, and/or the message-passing
76+
pathway respectively. This makes progressive training explicit — e.g. train `2b`, then `freeze:["2b"]`
77+
with `mode:"2b+3b"` to train only the three-center term, then `freeze:["2b","3b"]` with `mode:"full"` to
78+
train only env. If `freeze` is unset the historical default is kept: `"full"` freezes `["2b","3b"]`
79+
(train only env) and every other mode freezes nothing; pass `freeze:[]` to train everything.
80+
81+
## Consequences
82+
83+
- Heterogeneous species are handled in `OrbitalMapper.full_basis` (the union of all species' shells)
84+
with `mask_to_basis` zeroing absent channels, so tensors stay uniform (batched GEMM unchanged) and
85+
blocks come out in full_basis order like `E3Hamiltonian`. Single shared layouts are the special
86+
case where the masks are all-ones.
87+
- Hermiticity is structural (`P_{CB}=P_{BC}^†`, `D_C=D_C^†`), so upper/lower blocks stay consistent.
88+
- Rotation covariance follows from `wigner_D` on `P` plus rotation-invariant `D_C`.
89+
- Auxiliary rank (`N_l` projectors per `l`) is the accuracy/cost knob; the factorization rank of a
90+
triangle block is bounded by the projector count.
91+
- Assembly is fully vectorized for GPU: `P_{XC}` is built for all edges at once (CG selection +
92+
`wigner_D`), the triplet index (all `(A,C,B)` sharing center `C`) is built with
93+
`argsort`/`cumsum`/`repeat_interleave` with **no Python loop over centers**, target edges are
94+
matched with `searchsorted` (no `N_atoms^2` map), and `sum_C P_AC D_C P_CB` runs as one batched
95+
GEMM over all triangles. Cost tracks the triplet count `sum_C deg(C)^2`, inherent to three-center.
96+
- Changes to the assembler are compatibility-sensitive once wired into a trainable model and must be
97+
covered by `dptb/tests/test_threecenter.py`.

0 commit comments

Comments
 (0)