jaccpot 0.0.1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (50) hide show
  1. jaccpot/__init__.py +34 -0
  2. jaccpot/_typecheck.py +33 -0
  3. jaccpot/autodiff.py +36 -0
  4. jaccpot/basis/__init__.py +14 -0
  5. jaccpot/basis/base.py +56 -0
  6. jaccpot/basis/complex_sh.py +114 -0
  7. jaccpot/basis/real_sh.py +142 -0
  8. jaccpot/config.py +115 -0
  9. jaccpot/downward/__init__.py +1 -0
  10. jaccpot/downward/local_expansions.py +1301 -0
  11. jaccpot/nearfield/__init__.py +1 -0
  12. jaccpot/nearfield/near_field.py +3768 -0
  13. jaccpot/odisseo.py +90 -0
  14. jaccpot/operators/__init__.py +19 -0
  15. jaccpot/operators/complex_harmonics.py +205 -0
  16. jaccpot/operators/complex_ops.py +1918 -0
  17. jaccpot/operators/dtypes.py +5 -0
  18. jaccpot/operators/m2l_real_rot_scale.py +125 -0
  19. jaccpot/operators/multipole_utils.py +186 -0
  20. jaccpot/operators/real_harmonics.py +1719 -0
  21. jaccpot/operators/solidfmm_reference.py +145 -0
  22. jaccpot/operators/symmetric_tensors.py +117 -0
  23. jaccpot/pallas/__init__.py +20 -0
  24. jaccpot/pallas/m2l_core_z_real.py +152 -0
  25. jaccpot/pallas/nearfield_fused_leaf.py +593 -0
  26. jaccpot/py.typed +0 -0
  27. jaccpot/runtime/__init__.py +1 -0
  28. jaccpot/runtime/_adaptive_policy.py +973 -0
  29. jaccpot/runtime/_fmm_impl.py +13503 -0
  30. jaccpot/runtime/_interaction_cache.py +1279 -0
  31. jaccpot/runtime/_large_n_farfield.py +31 -0
  32. jaccpot/runtime/_large_n_nearfield.py +514 -0
  33. jaccpot/runtime/_large_n_pipeline.py +1925 -0
  34. jaccpot/runtime/_large_n_types.py +612 -0
  35. jaccpot/runtime/_nearfield_cache.py +94 -0
  36. jaccpot/runtime/_octree_adapter.py +161 -0
  37. jaccpot/runtime/_octree_fmm.py +729 -0
  38. jaccpot/runtime/dtypes.py +52 -0
  39. jaccpot/runtime/fmm.py +6 -0
  40. jaccpot/runtime/fmm_presets.py +134 -0
  41. jaccpot/runtime/reference.py +230 -0
  42. jaccpot/solver.py +1113 -0
  43. jaccpot/upward/__init__.py +1 -0
  44. jaccpot/upward/solidfmm_complex_tree_expansions.py +674 -0
  45. jaccpot/upward/tree_expansions.py +285 -0
  46. jaccpot-0.0.1.dist-info/METADATA +550 -0
  47. jaccpot-0.0.1.dist-info/RECORD +50 -0
  48. jaccpot-0.0.1.dist-info/WHEEL +5 -0
  49. jaccpot-0.0.1.dist-info/licenses/LICENSE +21 -0
  50. jaccpot-0.0.1.dist-info/top_level.txt +1 -0
jaccpot/__init__.py ADDED
@@ -0,0 +1,34 @@
1
+ """Jaccpot: high-level FMM solver APIs built on Yggdrasil artifacts."""
2
+
3
+ from ._typecheck import enable_runtime_typecheck
4
+
5
+ enable_runtime_typecheck()
6
+
7
+ from .autodiff import differentiable_gravitational_acceleration
8
+ from .basis import ComplexSHBasis, RealSHBasis
9
+ from .config import (
10
+ FarFieldConfig,
11
+ FMMAdvancedConfig,
12
+ FMMPreset,
13
+ MemoryObjective,
14
+ NearFieldConfig,
15
+ RuntimePolicyConfig,
16
+ TreeConfig,
17
+ )
18
+ from .odisseo import OdisseoFMMCoupler
19
+ from .solver import FastMultipoleMethod
20
+
21
+ __all__ = [
22
+ "FMMAdvancedConfig",
23
+ "FMMPreset",
24
+ "FarFieldConfig",
25
+ "FastMultipoleMethod",
26
+ "ComplexSHBasis",
27
+ "MemoryObjective",
28
+ "RealSHBasis",
29
+ "NearFieldConfig",
30
+ "OdisseoFMMCoupler",
31
+ "RuntimePolicyConfig",
32
+ "TreeConfig",
33
+ "differentiable_gravitational_acceleration",
34
+ ]
jaccpot/_typecheck.py ADDED
@@ -0,0 +1,33 @@
1
+ """Runtime type-check configuration for jaccpot."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import os
6
+ from typing import Any
7
+
8
+ _TYPECHECK_HOOK: Any = None
9
+
10
+
11
+ def _runtime_typecheck_enabled() -> bool:
12
+ raw = os.getenv("JACCPOT_RUNTIME_TYPECHECK", "0").strip().lower()
13
+ return raw not in {"0", "false", "no", "off"}
14
+
15
+
16
+ def enable_runtime_typecheck() -> bool:
17
+ """Enable package-wide jaxtyping+beartype checks for annotated callables."""
18
+ global _TYPECHECK_HOOK
19
+
20
+ if _TYPECHECK_HOOK is not None:
21
+ return True
22
+ if not _runtime_typecheck_enabled():
23
+ return False
24
+
25
+ from jaxtyping import install_import_hook
26
+
27
+ # Instruments submodule imports under `jaccpot` so annotated callables are
28
+ # checked by beartype with jaxtyping's shape/dtype semantics.
29
+ _TYPECHECK_HOOK = install_import_hook("jaccpot", typechecker="beartype.beartype")
30
+ return True
31
+
32
+
33
+ __all__ = ["enable_runtime_typecheck"]
jaccpot/autodiff.py ADDED
@@ -0,0 +1,36 @@
1
+ """Autodiff helpers for Jaccpot."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from typing import Optional, Tuple
6
+
7
+ import jax
8
+ import jax.numpy as jnp
9
+ from jaxtyping import Array
10
+
11
+
12
+ def differentiable_gravitational_acceleration(
13
+ positions: Array,
14
+ masses: Array,
15
+ *,
16
+ theta: float = 0.6,
17
+ G: float = 1.0,
18
+ softening: float = 1e-3,
19
+ bounds: Optional[Tuple[Array, Array]] = None,
20
+ leaf_size: int = 16,
21
+ max_order: int = 4,
22
+ ) -> Array:
23
+ """Differentiable gravitational accelerations via direct summation."""
24
+
25
+ del theta, bounds, leaf_size, max_order
26
+
27
+ diffs = positions[:, None, :] - positions[None, :, :]
28
+ dist2 = jnp.sum(diffs * diffs, axis=-1) + softening**2
29
+ inv_dist = jnp.where(dist2 > 0, jnp.power(dist2, -0.5), 0.0)
30
+ inv_dist3 = inv_dist**3
31
+ weights = masses[None, :] * inv_dist3
32
+ weights = weights * (1.0 - jnp.eye(positions.shape[0], dtype=positions.dtype))
33
+ return -G * jnp.einsum("ij,ijk->ik", weights, diffs)
34
+
35
+
36
+ __all__ = ["differentiable_gravitational_acceleration"]
@@ -0,0 +1,14 @@
1
+ """Basis abstractions for FMM coefficient representations."""
2
+
3
+ from .base import BasisInterface, BasisMetadata
4
+ from .complex_sh import ComplexSHBasis
5
+ from .real_sh import RealSHBasis, complex_to_real_coeffs, real_to_complex_coeffs
6
+
7
+ __all__ = [
8
+ "BasisInterface",
9
+ "BasisMetadata",
10
+ "ComplexSHBasis",
11
+ "RealSHBasis",
12
+ "complex_to_real_coeffs",
13
+ "real_to_complex_coeffs",
14
+ ]
jaccpot/basis/base.py ADDED
@@ -0,0 +1,56 @@
1
+ """Shared basis interface for FMM coefficient transforms and M2L kernels."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from dataclasses import dataclass
6
+ from typing import Protocol, runtime_checkable
7
+
8
+ from jaxtyping import Array
9
+
10
+
11
+ @runtime_checkable
12
+ class BasisInterface(Protocol):
13
+ """Minimal basis contract used by solver/runtime orchestration.
14
+
15
+ Implementations define coefficient layout metadata and batched transforms
16
+ needed by M2L operators.
17
+ """
18
+
19
+ name: str
20
+ p_max: int
21
+ coefficient_ordering: str
22
+ runtime_expansion_basis: str
23
+
24
+ def n_coeffs(self: "BasisInterface", p: int) -> int:
25
+ """Return number of packed coefficients for expansion order ``p``."""
26
+
27
+ def pack_coeffs(self: "BasisInterface", coeffs: Array, *, order: int) -> Array:
28
+ """Pack basis coefficients into the runtime 1D layout."""
29
+
30
+ def unpack_coeffs(self: "BasisInterface", packed: Array, *, order: int) -> Array:
31
+ """Unpack runtime 1D coefficient layout into structured basis form."""
32
+
33
+ def rotate_to_z(
34
+ self: "BasisInterface", coeffs: Array, directions: Array, *, order: int
35
+ ) -> Array:
36
+ """Rotate batched coefficients into a frame where direction maps to ``+z``."""
37
+
38
+ def rotate_from_z(
39
+ self: "BasisInterface", coeffs: Array, directions: Array, *, order: int
40
+ ) -> Array:
41
+ """Rotate batched coefficients back from the ``+z`` frame."""
42
+
43
+ def m2l_rot_scale(
44
+ self: "BasisInterface", sources: Array, deltas: Array, *, order: int
45
+ ) -> Array:
46
+ """Evaluate batched M2L translation in the basis' native convention."""
47
+
48
+
49
+ @dataclass(frozen=True)
50
+ class BasisMetadata:
51
+ """Static metadata helper for basis implementations."""
52
+
53
+ name: str
54
+ p_max: int
55
+ coefficient_ordering: str
56
+ runtime_expansion_basis: str
@@ -0,0 +1,114 @@
1
+ """Complex spherical-harmonic basis adapter for existing solidfmm kernels."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from dataclasses import dataclass
6
+
7
+ import jax
8
+ import jax.numpy as jnp
9
+ from jaxtyping import Array
10
+
11
+ from jaccpot.operators.complex_ops import (
12
+ complex_rotation_blocks_from_z_solidfmm_batch,
13
+ complex_rotation_blocks_to_z_solidfmm_batch,
14
+ m2l_complex_reference_batch,
15
+ )
16
+ from jaccpot.operators.real_harmonics import sh_size
17
+
18
+
19
+ @dataclass(frozen=True)
20
+ class ComplexSHBasis:
21
+ """Adapter exposing the current solidfmm complex basis through a common API.
22
+
23
+ Coefficients use the existing packed solidfmm layout (ell-major blocks with
24
+ ``m=-ell..+ell`` for each degree).
25
+ """
26
+
27
+ p_max: int = 32
28
+ name: str = "complex"
29
+ coefficient_ordering: str = "ell-major, m=-ell..+ell (packed complex SH)"
30
+ runtime_expansion_basis: str = "solidfmm"
31
+
32
+ def n_coeffs(self: "ComplexSHBasis", p: int) -> int:
33
+ """Return packed coefficient count for order ``p``."""
34
+ return int(sh_size(int(p)))
35
+
36
+ def pack_coeffs(self: "ComplexSHBasis", coeffs: Array, *, order: int) -> Array:
37
+ """Validate and return packed solidfmm complex coefficients."""
38
+ coeffs_arr = jnp.asarray(coeffs)
39
+ expected = self.n_coeffs(order)
40
+ if int(coeffs_arr.shape[-1]) != expected:
41
+ raise ValueError(
42
+ f"expected last dimension {expected} for order={int(order)}, got {coeffs_arr.shape[-1]}"
43
+ )
44
+ return coeffs_arr
45
+
46
+ def unpack_coeffs(self: "ComplexSHBasis", packed: Array, *, order: int) -> Array:
47
+ """Return packed coefficients (complex basis already uses packed layout)."""
48
+ return self.pack_coeffs(packed, order=order)
49
+
50
+ def rotate_to_z(
51
+ self: "ComplexSHBasis", coeffs: Array, directions: Array, *, order: int
52
+ ) -> Array:
53
+ """Rotate batched multipoles into a frame aligned to ``+z``."""
54
+ coeffs_arr = self.pack_coeffs(coeffs, order=order)
55
+ x, y, z = self._split_xyz(directions)
56
+ to_blocks = complex_rotation_blocks_to_z_solidfmm_batch(
57
+ x,
58
+ y,
59
+ z,
60
+ order=int(order),
61
+ dtype=coeffs_arr.dtype,
62
+ )
63
+ return self._apply_rotation_blocks(coeffs_arr, to_blocks)
64
+
65
+ def rotate_from_z(
66
+ self: "ComplexSHBasis", coeffs: Array, directions: Array, *, order: int
67
+ ) -> Array:
68
+ """Rotate batched multipoles from the ``+z`` frame back to world frame."""
69
+ coeffs_arr = self.pack_coeffs(coeffs, order=order)
70
+ x, y, z = self._split_xyz(directions)
71
+ from_blocks = complex_rotation_blocks_from_z_solidfmm_batch(
72
+ x,
73
+ y,
74
+ z,
75
+ order=int(order),
76
+ dtype=coeffs_arr.dtype,
77
+ )
78
+ return self._apply_rotation_blocks(coeffs_arr, from_blocks)
79
+
80
+ def m2l_rot_scale(
81
+ self: "ComplexSHBasis", sources: Array, deltas: Array, *, order: int
82
+ ) -> Array:
83
+ """Run existing batched complex M2L kernel for source multipoles."""
84
+ src = self.pack_coeffs(sources, order=order)
85
+ delta_arr = jnp.asarray(deltas)
86
+ if delta_arr.ndim != 2 or int(delta_arr.shape[1]) != 3:
87
+ raise ValueError("deltas must have shape (batch, 3)")
88
+ return m2l_complex_reference_batch(src, delta_arr, order=int(order))
89
+
90
+ @staticmethod
91
+ def _split_xyz(directions: Array) -> tuple[Array, Array, Array]:
92
+ directions_arr = jnp.asarray(directions)
93
+ if directions_arr.ndim != 2 or int(directions_arr.shape[1]) != 3:
94
+ raise ValueError("directions must have shape (batch, 3)")
95
+ return directions_arr[:, 0], directions_arr[:, 1], directions_arr[:, 2]
96
+
97
+ @staticmethod
98
+ def _apply_rotation_blocks(coeffs: Array, blocks: list[Array]) -> Array:
99
+ offsets = [0]
100
+ for ell, block in enumerate(blocks):
101
+ width = int(block.shape[-1])
102
+ if width != 2 * ell + 1:
103
+ raise ValueError("rotation block width does not match ell")
104
+ offsets.append(offsets[-1] + width)
105
+
106
+ def rotate_one(row: Array, row_blocks: list[Array]) -> Array:
107
+ parts = []
108
+ for ell, block in enumerate(row_blocks):
109
+ start = offsets[ell]
110
+ end = offsets[ell + 1]
111
+ parts.append(block @ row[start:end])
112
+ return jnp.concatenate(parts, axis=0)
113
+
114
+ return jax.vmap(rotate_one)(coeffs, blocks)
@@ -0,0 +1,142 @@
1
+ """Real spherical-harmonic basis layout and complex/real conversion helpers."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from dataclasses import dataclass
6
+
7
+ import jax.numpy as jnp
8
+ from jaxtyping import Array
9
+
10
+
11
+ def _idx_nm(n: int, m: int) -> int:
12
+ """Packed index for degree ``n`` and order ``m`` in ``m=-n..n`` layout."""
13
+ if abs(int(m)) > int(n):
14
+ raise ValueError("|m| must be <= n")
15
+ return int(n) * int(n) + (int(m) + int(n))
16
+
17
+
18
+ def n_real_sh_coeffs(order: int) -> int:
19
+ """Number of packed real SH coefficients for ``order``."""
20
+ p = int(order)
21
+ if p < 0:
22
+ raise ValueError("order must be non-negative")
23
+ return (p + 1) * (p + 1)
24
+
25
+
26
+ def complex_to_real_coeffs(complex_coeffs: Array, *, order: int) -> Array:
27
+ """Convert packed complex SH coefficients to packed real SH coefficients.
28
+
29
+ This conversion assumes Condon-Shortley style paired complex coefficients
30
+ where each ``(n, m>0)`` pair is transformed into two real coefficients
31
+ ``(n, +m)`` and ``(n, -m)``.
32
+ """
33
+
34
+ coeffs = jnp.asarray(complex_coeffs)
35
+ expected = n_real_sh_coeffs(order)
36
+ if int(coeffs.shape[-1]) != expected:
37
+ raise ValueError(
38
+ f"expected last dimension {expected} for order={int(order)}, got {coeffs.shape[-1]}"
39
+ )
40
+
41
+ out = jnp.zeros(coeffs.shape, dtype=coeffs.real.dtype)
42
+ sqrt2 = jnp.sqrt(jnp.asarray(2.0, dtype=coeffs.real.dtype))
43
+
44
+ for n in range(int(order) + 1):
45
+ idx0 = _idx_nm(n, 0)
46
+ out = out.at[..., idx0].set(jnp.real(coeffs[..., idx0]))
47
+ for m in range(1, n + 1):
48
+ idx_p = _idx_nm(n, m)
49
+ idx_n = _idx_nm(n, -m)
50
+ sign = -1.0 if (m % 2) else 1.0
51
+ c_p = coeffs[..., idx_p]
52
+ c_n = coeffs[..., idx_n]
53
+ r_p = (sign * c_p + c_n) / sqrt2
54
+ r_n = (sign * c_p - c_n) / (1j * sqrt2)
55
+ out = out.at[..., idx_p].set(jnp.real(r_p))
56
+ out = out.at[..., idx_n].set(jnp.real(r_n))
57
+
58
+ return out
59
+
60
+
61
+ def real_to_complex_coeffs(real_coeffs: Array, *, order: int) -> Array:
62
+ """Convert packed real SH coefficients to packed complex SH coefficients."""
63
+
64
+ coeffs = jnp.asarray(real_coeffs)
65
+ expected = n_real_sh_coeffs(order)
66
+ if int(coeffs.shape[-1]) != expected:
67
+ raise ValueError(
68
+ f"expected last dimension {expected} for order={int(order)}, got {coeffs.shape[-1]}"
69
+ )
70
+
71
+ out = jnp.zeros(coeffs.shape, dtype=jnp.result_type(coeffs.dtype, jnp.complex64))
72
+ sqrt2 = jnp.sqrt(jnp.asarray(2.0, dtype=coeffs.dtype))
73
+
74
+ for n in range(int(order) + 1):
75
+ idx0 = _idx_nm(n, 0)
76
+ out = out.at[..., idx0].set(coeffs[..., idx0].astype(out.dtype))
77
+ for m in range(1, n + 1):
78
+ idx_p = _idx_nm(n, m)
79
+ idx_n = _idx_nm(n, -m)
80
+ sign = -1.0 if (m % 2) else 1.0
81
+ r_p = coeffs[..., idx_p]
82
+ r_n = coeffs[..., idx_n]
83
+ c_p = sign * (r_p + 1j * r_n) / sqrt2
84
+ c_n = (r_p - 1j * r_n) / sqrt2
85
+ out = out.at[..., idx_p].set(c_p.astype(out.dtype))
86
+ out = out.at[..., idx_n].set(c_n.astype(out.dtype))
87
+
88
+ return out
89
+
90
+
91
+ @dataclass(frozen=True)
92
+ class RealSHBasis:
93
+ """Packed real spherical-harmonic basis metadata and layout helpers."""
94
+
95
+ p_max: int = 32
96
+ name: str = "real"
97
+ coefficient_ordering: str = "ell-major, m=-ell..+ell (packed real SH)"
98
+ runtime_expansion_basis: str = "solidfmm"
99
+
100
+ def n_coeffs(self: "RealSHBasis", p: int) -> int:
101
+ """Return packed coefficient count for expansion order ``p``."""
102
+ return n_real_sh_coeffs(int(p))
103
+
104
+ def pack_coeffs(self: "RealSHBasis", coeffs: Array, *, order: int) -> Array:
105
+ """Validate and return packed real SH coefficients."""
106
+ arr = jnp.asarray(coeffs)
107
+ expected = self.n_coeffs(order)
108
+ if int(arr.shape[-1]) != expected:
109
+ raise ValueError(
110
+ f"expected last dimension {expected} for order={int(order)}, got {arr.shape[-1]}"
111
+ )
112
+ return arr
113
+
114
+ def unpack_coeffs(self: "RealSHBasis", packed: Array, *, order: int) -> Array:
115
+ """Return packed coefficients (real SH uses the packed runtime layout)."""
116
+ return self.pack_coeffs(packed, order=order)
117
+
118
+ def rotate_to_z(
119
+ self: "RealSHBasis", coeffs: Array, directions: Array, *, order: int
120
+ ) -> Array:
121
+ """Rotate real SH coefficients into a z-aligned frame (not yet implemented)."""
122
+ raise NotImplementedError("real SH rotations are implemented in Stage J3")
123
+
124
+ def rotate_from_z(
125
+ self: "RealSHBasis", coeffs: Array, directions: Array, *, order: int
126
+ ) -> Array:
127
+ """Rotate real SH coefficients back from a z-aligned frame."""
128
+ raise NotImplementedError("real SH rotations are implemented in Stage J3")
129
+
130
+ def m2l_rot_scale(
131
+ self: "RealSHBasis", sources: Array, deltas: Array, *, order: int
132
+ ) -> Array:
133
+ """Translate real SH multipoles to locals via rotate/scale M2L path."""
134
+ raise NotImplementedError("real SH rotate+scale M2L is implemented in Stage J3")
135
+
136
+
137
+ __all__ = [
138
+ "RealSHBasis",
139
+ "complex_to_real_coeffs",
140
+ "real_to_complex_coeffs",
141
+ "n_real_sh_coeffs",
142
+ ]
jaccpot/config.py ADDED
@@ -0,0 +1,115 @@
1
+ """Preset-first configuration model for Jaccpot."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from dataclasses import dataclass
6
+ from enum import Enum
7
+ from typing import Any, Literal, Optional
8
+
9
+ BASIS_DOC = "Preferred production basis is 'solidfmm'."
10
+ FARFIELD_MODE_DOC = (
11
+ "For large_n_gpu production, far-field execution is canonicalized to "
12
+ "'pair_grouped'."
13
+ )
14
+ NEARFIELD_MODE_DOC = (
15
+ "For large_n_gpu production, near-field execution is canonicalized to "
16
+ "'bucketed'."
17
+ )
18
+ MEMORY_OBJECTIVE_DOC = (
19
+ "For large_n_gpu production, memory objective is canonicalized to "
20
+ "'minimum_memory'."
21
+ )
22
+
23
+ Basis = Literal["cartesian", "solidfmm", "complex", "real"]
24
+ FarFieldMode = Literal["auto", "pair_grouped", "class_major"]
25
+ NearFieldMode = Literal["auto", "baseline", "bucketed"]
26
+ MemoryObjective = Literal["balanced", "throughput", "minimum_memory"]
27
+ FMMExecutionBackend = Literal["auto", "radix", "octree"]
28
+
29
+
30
+ class FMMPreset(str, Enum):
31
+ """User-facing quality/speed presets."""
32
+
33
+ FAST = "fast"
34
+ BALANCED = "balanced"
35
+ ACCURATE = "accurate"
36
+ LARGE_N_GPU = "large_n_gpu"
37
+
38
+
39
+ @dataclass(frozen=True)
40
+ class TreeConfig:
41
+ """Tree-construction overrides for advanced runtime tuning."""
42
+
43
+ tree_type: Optional[str] = None
44
+ mode: Optional[str] = None
45
+ leaf_target: Optional[int] = None
46
+ refine_local: Optional[bool] = None
47
+ max_refine_levels: Optional[int] = None
48
+ aspect_threshold: Optional[float] = None
49
+
50
+
51
+ @dataclass(frozen=True)
52
+ class FarFieldConfig:
53
+ """Far-field interaction and translation-kernel overrides."""
54
+
55
+ grouped_interactions: Optional[bool] = None
56
+ mode: FarFieldMode = "auto"
57
+ rotation: Optional[str] = None
58
+ m2l_chunk_size: Optional[int] = None
59
+ l2l_chunk_size: Optional[int] = None
60
+ streamed_far_pairs: Optional[bool] = None
61
+ mixed_order: bool = False
62
+ mixed_order_min_order: Optional[int] = None
63
+
64
+
65
+ @dataclass(frozen=True)
66
+ class NearFieldConfig:
67
+ """Near-field direct-interaction strategy overrides."""
68
+
69
+ mode: NearFieldMode = "auto"
70
+ edge_chunk_size: int = 256
71
+ precompute_scatter_schedules: bool = True
72
+
73
+
74
+ @dataclass(frozen=True)
75
+ class RuntimePolicyConfig:
76
+ """Execution-policy overrides for tree build and traversal.
77
+
78
+ Notes:
79
+ - `runtime_path='legacy'` is deprecated and will be removed.
80
+ - For `preset='large_n_gpu'`, runtime policy is canonicalized to the
81
+ production low-memory fast path (minimum_memory + streamed pair_grouped
82
+ + bucketed nearfield).
83
+ """
84
+
85
+ execution_backend: FMMExecutionBackend = "auto"
86
+ host_refine_mode: str = "auto"
87
+ fail_fast: bool = False
88
+ jit_tree: Optional[bool] = None
89
+ jit_traversal: Optional[bool] = None
90
+ memory_objective: MemoryObjective = "balanced"
91
+ memory_budget_bytes: Optional[int] = None
92
+ max_pair_queue: Optional[int] = None
93
+ pair_process_block: Optional[int] = None
94
+ traversal_config: Optional[Any] = None
95
+ enable_interaction_cache: bool = True
96
+ retain_traversal_result: bool = True
97
+ retain_interactions: bool = True
98
+ prepare_stage_memory_split_enabled: Optional[bool] = None
99
+ autotune_m2l_chunk: bool = False
100
+ precompute_grouped_class_segments: Optional[bool] = None
101
+ grouped_schedule_budget_bytes: Optional[int] = None
102
+ nearfield_schedule_item_cap: Optional[int] = None
103
+ upward_leaf_batch_size: Optional[int] = None
104
+
105
+
106
+ @dataclass(frozen=True)
107
+ class FMMAdvancedConfig:
108
+ """Aggregate container for all advanced FMM override groups."""
109
+
110
+ tree: TreeConfig = TreeConfig()
111
+ farfield: FarFieldConfig = FarFieldConfig()
112
+ nearfield: NearFieldConfig = NearFieldConfig()
113
+ runtime: RuntimePolicyConfig = RuntimePolicyConfig()
114
+ mac_type: Optional[str] = None
115
+ dehnen_radius_scale: float = 1.0
@@ -0,0 +1 @@
1
+ """Downward-sweep operators for local-expansion propagation."""