# 二電子積分と J/K 戦略

量子化学計算で最大の性能レバーは、**二電子積分をどう扱うか**です。それらは $\mathcal{O}(N^4)$ 個あり、各 SCF
サイクルがそれらをクーロン（**J**）と交換（**K**）行列に縮約します。qc-rs はその選択を**単一の**キーワード
`ints(eri=...)` で公開し、その選択は前 3 章のあらゆる並列レベルと相互作用します。本章は、正しいものを選べるよう
選択肢を地図化します。

## 理論:なぜ積分の扱いが支配するのか

$N$ 個の基底関数に対し、一意な二電子積分 $(\mu\nu|\lambda\sigma)$ は形式上 $N^4/8$ 個あります。$N = 500$ で
約 80 億個 — しばしば*保存*するには多すぎ、*再計算*するには高価です。以降のあらゆる手法（SCF, MP2, …）はここで
律速されるので、戦略の問いは実際には:**保存するか、再計算するか、ディスクに置くか、近似するか？** です。各答えは
メモリと CPU 時間を異なる形でトレードオフするので、単一の最良の選択はありません — 分子とハードウェア次第です。

## 統一された `eri=` 軸

すべては `ints(...)` の 1 キーワードで、2 つのファミリに分かれます。

### 4 中心ファミリ — 厳密な積分

真の $(\mu\nu|\lambda\sigma)$ 積分を使い（近似なし）、**ビット単位で同一**のエネルギーを与え、*積分がどこに
あるか*だけが異なります:

| `eri=` | 戦略 | 適する場面 |
|---|---|---|
| `4c-auto` *(既定)* | メモリ梯子を歩く:incore → ramdisk → disk → direct | とりあえず動く |
| `4c-incore` | **全**積分を RAM に保存 | 小〜中、RAM に余裕（最速） |
| `4c-direct` | 毎サイクル積分を**再計算**、何も保存しない | 大、RAM 制限 |
| `4c-disk` | 積分を一度ディスクに書き、毎サイクルストリーム | アウトオブコア、速いスクラッチディスク |
| `4c-ramdisk` | 疎ディスク符号化を RAM に保持 | 大/疎、MPI ランクに分散 |

`4c-auto` は安全な既定 — 収まる最もメモリを食う選択肢を選び、優雅にフォールバックします。RI（下記）を**決して**
選びません;RI は明示的にのみ選ばれます。

### RI（密度フィッティング）ファミリ — 近似、はるかに安価

**RI**（恒等分解、[ポスト SCF の章](../20-guide/post-scf.md) より）は 4 指標積分を**補助基底**を介して分解し、
小さく制御された フィッティング誤差と引き換えにコストを劇的に削ります。2 つの副ファミリは、フィットした factor を
**保存**するか**再計算**するかで異なります:

| `eri=` | 戦略 | 注記 |
|---|---|---|
| `ri-ram` | 白色化 RI factor を**一度**構築し RAM に保持（MPI で μ 分散） | incore 速度・recompute メモリ — 規模での store-`B` 既定 |
| `ri-recomp` | 毎サイクル 3 中心積分を**再計算**、factor を保存しない | メモリ倹約の RI |
| `ri-recomp-disk` | 再計算 + より小さな占有 factor をディスクに書く | アウトオブコア RI |

GPU メンバー（`ri-cuda`, `ri-recomp-cuda`, `ri-ram-cuda`）は [GPU の章](gpu-cuda.md) にあります。RI は補助基底を
必要とします;`rijk=` を与えないと、qc-rs は軌道基底から既定の JK-fit 補助を自動導出します。

## 厳密 対 RI:フィッティング誤差、検証

4 中心戦略は厳密で最後の桁まで一致します;RI 戦略は**小さなフィッティング誤差**を持ちますがはるかに安価です。
水/cc-pVDZ（RHF）で:

```python
import qc
water = "O 0 0 0.117; H 0 0.757 -0.469; H 0 -0.757 -0.469"

for eri in ("4c-incore", "4c-direct", "ri-ram", "ri-recomp"):
    e = qc.chk.new(atom=water, ao="cc-pvdz", unit="angstrom",
                   rijk="cc-pvdz-jkfit").ints(eri=eri).scf(ref="r").run().scf.energy
    print(f"{eri:11} {e:.6f}")
# 4c-incore   -76.026794   ← 厳密
# 4c-direct   -76.026794   ← 厳密（incore とビット単位で同一）
# ri-ram      -76.026773   ← RI: +2.1e-5 フィッティング誤差
# ri-recomp   -76.026773   ← RI: ri-ram と同一
```

2 つの 4 中心戦略は**ビット単位で同一**（−76.026794）;2 つの RI 戦略は**互いに同一**（−76.026773）で、厳密とは
わずか **2.1 × 10⁻⁵ Ha** だけ異なります — 化学的精度をはるかに下回る RI フィッティング誤差です。だから RI は
無視できる制御された誤差で、大きな速度/メモリの利得を買います。

## 戦略の選び方

実務的な決定ガイド:

- **既定 / 不明** → `4c-auto`。うまく選び、RAM が厳しければフォールバック。
- **小〜中、RAM に余裕、厳密で最速が欲しい** → `4c-incore`。
- **大、RAM 制限、厳密が欲しい** → `4c-direct`（再計算）または `4c-disk`（アウトオブコア）。
- **大、大きな速度/メモリの利得が欲しく ~1e-5 Ha を許容できる** → **RI** ファミリ（factor が RAM に収まれば
  `ri-ram`、収まらなければ `ri-recomp`）。規模での通常の選択で、RI-MP2 の*唯一の*経路。
- **GPU 利用可能** → `4c-cuda` / `ri-cuda`。

RI は MPI とも合成して 1 ノードのメモリの壁を破ります（`ri-ram` は factor を AO 番号で分散するので、ランク
あたりメモリ ≈ `全体 / ランク数`、[MPI の章](mpi-and-interconnects.md)）。

## 補助基底と関連ノブ

```python
qc.chk.new(..., rijk="cc-pvdz-jkfit")     # RI J/K の JK-fit 補助（既定は自動導出）
qc.chk.new(..., ric="cc-pvdz-ri/mp2fit")  # RI-MP2 の相関フィット補助（ポスト SCF の章）
```

`eri="none"` は二電子構築を完全に飛ばします（1 電子のみのワークフロー用）。アウトオブコアのノブ（疎レイアウト、
ドロップ許容差、I/O モード）は `integral.disk.*` 下の `iop` キーです — [IOP リファレンス](../40-reference/iop.md)
参照。

## 総合例

```python
import qc
water = "O 0 0 0.117; H 0 0.757 -0.469; H 0 -0.757 -0.469"

exact = qc.chk.new(atom=water, ao="cc-pvdz", unit="angstrom").ints(eri="4c-incore").scf(ref="r").run()
ri    = qc.chk.new(atom=water, ao="cc-pvdz", unit="angstrom",
                   rijk="cc-pvdz-jkfit").ints(eri="ri-ram").scf(ref="r").run()

print(f"exact (4c) : {exact.scf.energy:.6f}")   # -76.026794
print(f"RI         : {ri.scf.energy:.6f}")       # -76.026773
print(f"fitting err: {abs(exact.scf.energy - ri.scf.energy):.2e} Ha")   # 2.1e-05
```

:::{exercise}
:label: ex-eri

1. 大きな分子の cc-pVTZ 計算が `4c-incore` でメモリ不足エラーで落ちます。走らせられる異なる `eri=` 戦略を 2 つ、
   各々のトレードオフとともに挙げなさい。
2. `ri-ram` と `ri-recomp` が*互いに*同じエネルギーを与えるのに、`4c-incore` とはわずかに異なるのはなぜですか。
3. RI-MP2 を走らせたいです。どの `eri=` ファミリが必須で、どの追加の基底キーワードを設定せねばなりませんか。
:::

:::{solution} ex-eri
:class: dropdown

1. (a) `4c-direct` — 積分を毎サイクル再計算（厳密、保存なし、ただしサイクルあたり CPU が増える）;(b) `ri-ram`
   または `ri-recomp` — 密度フィッティング（はるかに少ないメモリと CPU、~1e-5 Ha のフィッティング誤差で）。
   速いスクラッチディスクがあれば `4c-disk` も第 3 の選択肢。
2. どちらも **RI**（密度フィッティング） — *同じ*補助基底近似を使うので同じフィッティング誤差を共有します;
   フィットした factor を保存するか再計算するかだけが異なり、結果は変わりません。`4c-incore` は**厳密な**積分を
   使うので、そのフィッティング誤差がありません。
3. **RI** ファミリが必須です（RI-MP2 に非 RI 経路はない）。JK-fit の `rijk=` とは別に、**`ric=`** 相関フィット
   補助基底（例 `ric="cc-pvdz-ri/mp2fit"`）を設定せねばなりません。
:::

これで **Part IV** は完結です。qc-rs 計算を CPU スレッド・クラスタ上の MPI ランク・NVIDIA GPU にわたって
スケールさせ、分子とハードウェアに合った積分戦略を選べます。ここから、[リファレンス](../40-reference/mol-spec.md)
の各節が、あらゆる入力とオプションの正確な詳細を与えます。
