jaccpot 0.0.1__tar.gz
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.
- jaccpot-0.0.1/LICENSE +21 -0
- jaccpot-0.0.1/PKG-INFO +550 -0
- jaccpot-0.0.1/README.md +512 -0
- jaccpot-0.0.1/jaccpot/__init__.py +34 -0
- jaccpot-0.0.1/jaccpot/_typecheck.py +33 -0
- jaccpot-0.0.1/jaccpot/autodiff.py +36 -0
- jaccpot-0.0.1/jaccpot/basis/__init__.py +14 -0
- jaccpot-0.0.1/jaccpot/basis/base.py +56 -0
- jaccpot-0.0.1/jaccpot/basis/complex_sh.py +114 -0
- jaccpot-0.0.1/jaccpot/basis/real_sh.py +142 -0
- jaccpot-0.0.1/jaccpot/config.py +115 -0
- jaccpot-0.0.1/jaccpot/downward/__init__.py +1 -0
- jaccpot-0.0.1/jaccpot/downward/local_expansions.py +1301 -0
- jaccpot-0.0.1/jaccpot/nearfield/__init__.py +1 -0
- jaccpot-0.0.1/jaccpot/nearfield/near_field.py +3768 -0
- jaccpot-0.0.1/jaccpot/odisseo.py +90 -0
- jaccpot-0.0.1/jaccpot/operators/__init__.py +19 -0
- jaccpot-0.0.1/jaccpot/operators/complex_harmonics.py +205 -0
- jaccpot-0.0.1/jaccpot/operators/complex_ops.py +1918 -0
- jaccpot-0.0.1/jaccpot/operators/dtypes.py +5 -0
- jaccpot-0.0.1/jaccpot/operators/m2l_real_rot_scale.py +125 -0
- jaccpot-0.0.1/jaccpot/operators/multipole_utils.py +186 -0
- jaccpot-0.0.1/jaccpot/operators/real_harmonics.py +1719 -0
- jaccpot-0.0.1/jaccpot/operators/solidfmm_reference.py +145 -0
- jaccpot-0.0.1/jaccpot/operators/symmetric_tensors.py +117 -0
- jaccpot-0.0.1/jaccpot/pallas/__init__.py +20 -0
- jaccpot-0.0.1/jaccpot/pallas/m2l_core_z_real.py +152 -0
- jaccpot-0.0.1/jaccpot/pallas/nearfield_fused_leaf.py +593 -0
- jaccpot-0.0.1/jaccpot/py.typed +0 -0
- jaccpot-0.0.1/jaccpot/runtime/__init__.py +1 -0
- jaccpot-0.0.1/jaccpot/runtime/_adaptive_policy.py +973 -0
- jaccpot-0.0.1/jaccpot/runtime/_fmm_impl.py +13503 -0
- jaccpot-0.0.1/jaccpot/runtime/_interaction_cache.py +1279 -0
- jaccpot-0.0.1/jaccpot/runtime/_large_n_farfield.py +31 -0
- jaccpot-0.0.1/jaccpot/runtime/_large_n_nearfield.py +514 -0
- jaccpot-0.0.1/jaccpot/runtime/_large_n_pipeline.py +1925 -0
- jaccpot-0.0.1/jaccpot/runtime/_large_n_types.py +612 -0
- jaccpot-0.0.1/jaccpot/runtime/_nearfield_cache.py +94 -0
- jaccpot-0.0.1/jaccpot/runtime/_octree_adapter.py +161 -0
- jaccpot-0.0.1/jaccpot/runtime/_octree_fmm.py +729 -0
- jaccpot-0.0.1/jaccpot/runtime/dtypes.py +52 -0
- jaccpot-0.0.1/jaccpot/runtime/fmm.py +6 -0
- jaccpot-0.0.1/jaccpot/runtime/fmm_presets.py +134 -0
- jaccpot-0.0.1/jaccpot/runtime/reference.py +230 -0
- jaccpot-0.0.1/jaccpot/solver.py +1113 -0
- jaccpot-0.0.1/jaccpot/upward/__init__.py +1 -0
- jaccpot-0.0.1/jaccpot/upward/solidfmm_complex_tree_expansions.py +674 -0
- jaccpot-0.0.1/jaccpot/upward/tree_expansions.py +285 -0
- jaccpot-0.0.1/jaccpot.egg-info/PKG-INFO +550 -0
- jaccpot-0.0.1/jaccpot.egg-info/SOURCES.txt +59 -0
- jaccpot-0.0.1/jaccpot.egg-info/dependency_links.txt +1 -0
- jaccpot-0.0.1/jaccpot.egg-info/requires.txt +15 -0
- jaccpot-0.0.1/jaccpot.egg-info/top_level.txt +1 -0
- jaccpot-0.0.1/pyproject.toml +98 -0
- jaccpot-0.0.1/setup.cfg +4 -0
- jaccpot-0.0.1/tests/test_adaptive_order_runtime.py +338 -0
- jaccpot-0.0.1/tests/test_adaptive_policy_runtime.py +470 -0
- jaccpot-0.0.1/tests/test_force_scale_runtime.py +102 -0
- jaccpot-0.0.1/tests/test_gravity_vs_direct.py +108 -0
- jaccpot-0.0.1/tests/test_real_basis_runtime.py +105 -0
- jaccpot-0.0.1/tests/test_real_sh_roundtrip.py +55 -0
jaccpot-0.0.1/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 AstroAI Lab
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
jaccpot-0.0.1/PKG-INFO
ADDED
|
@@ -0,0 +1,550 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: jaccpot
|
|
3
|
+
Version: 0.0.1
|
|
4
|
+
Summary: JAX Fast Multipole Method solver package built on yggdrax tree artifacts
|
|
5
|
+
Author-email: AstroAI Lab <astroai@iwr.uni-heidelberg.de>, Tobias Buck <tobias.buck@iwr.uni-heidelberg.de>
|
|
6
|
+
Maintainer-email: AstroAI Lab <astroai@iwr.uni-heidelberg.de>
|
|
7
|
+
License-Expression: MIT
|
|
8
|
+
Project-URL: Homepage, https://github.com/AstroAI-Lab/jaccpot
|
|
9
|
+
Project-URL: Repository, https://github.com/AstroAI-Lab/jaccpot
|
|
10
|
+
Project-URL: Issues, https://github.com/AstroAI-Lab/jaccpot/issues
|
|
11
|
+
Project-URL: Documentation, https://github.com/AstroAI-Lab/jaccpot#readme
|
|
12
|
+
Keywords: fmm,n-body,jax,gravity,treecode
|
|
13
|
+
Classifier: Development Status :: 3 - Alpha
|
|
14
|
+
Classifier: Intended Audience :: Science/Research
|
|
15
|
+
Classifier: Programming Language :: Python :: 3
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
19
|
+
Classifier: Topic :: Scientific/Engineering :: Physics
|
|
20
|
+
Requires-Python: >=3.10
|
|
21
|
+
Description-Content-Type: text/markdown
|
|
22
|
+
License-File: LICENSE
|
|
23
|
+
Requires-Dist: jax[cuda]>=0.8.0
|
|
24
|
+
Requires-Dist: jaxlib>=0.4.0
|
|
25
|
+
Requires-Dist: jaxtyping>=0.2.23
|
|
26
|
+
Requires-Dist: beartype>=0.14.0
|
|
27
|
+
Requires-Dist: yggdrax<0.1.0,>=0.0.1
|
|
28
|
+
Provides-Extra: dev
|
|
29
|
+
Requires-Dist: black>=24.8.0; extra == "dev"
|
|
30
|
+
Requires-Dist: isort>=5.13.2; extra == "dev"
|
|
31
|
+
Requires-Dist: pytest>=8.3.0; extra == "dev"
|
|
32
|
+
Requires-Dist: pytest-cov>=5.0.0; extra == "dev"
|
|
33
|
+
Requires-Dist: pytest-xdist>=3.6.0; extra == "dev"
|
|
34
|
+
Requires-Dist: pre-commit>=3.8.0; extra == "dev"
|
|
35
|
+
Requires-Dist: build>=1.2.2; extra == "dev"
|
|
36
|
+
Requires-Dist: twine>=5.1.1; extra == "dev"
|
|
37
|
+
Dynamic: license-file
|
|
38
|
+
|
|
39
|
+
# jaccpot
|
|
40
|
+
|
|
41
|
+

|
|
42
|
+

|
|
43
|
+

|
|
44
|
+

|
|
45
|
+

|
|
46
|
+

|
|
47
|
+

|
|
48
|
+
|
|
49
|
+
<p align="center">
|
|
50
|
+
<img src="./jaccpot.png" alt="jaccpot Logo" width="420" />
|
|
51
|
+
</p>
|
|
52
|
+
|
|
53
|
+
`jaccpot` is a JAX-first Fast Multipole Method (FMM) package for N-body gravity
|
|
54
|
+
and related hierarchical interaction problems. It provides multipole/local
|
|
55
|
+
expansion operators, near/far-field kernels, and a preset-driven high-level
|
|
56
|
+
solver API.
|
|
57
|
+
|
|
58
|
+
Tree construction and traversal artifacts are provided by the companion package
|
|
59
|
+
`yggdrax`.
|
|
60
|
+
|
|
61
|
+
## Features
|
|
62
|
+
|
|
63
|
+
- High-level `FastMultipoleMethod` API with `fast`, `balanced`, `accurate`, and `large_n_gpu` presets
|
|
64
|
+
- Configurable expansion basis (`complex`/`solidfmm`, `real`, `cartesian`)
|
|
65
|
+
- Pure-JAX real spherical harmonic rotate+scale M2L path
|
|
66
|
+
- Adaptive-order far-field evaluation with fixed `p_gears` buckets
|
|
67
|
+
- Optional topology reuse for multiple nearby timesteps
|
|
68
|
+
- Optional Pallas acceleration for the real-basis z-translation hotspot
|
|
69
|
+
- Modular runtime with grouped/dense interaction pathways
|
|
70
|
+
- Near-field and far-field execution paths with optional prepared state reuse
|
|
71
|
+
- Explicit octree execution backend for `basis="solidfmm"`
|
|
72
|
+
- Differentiable gravitational acceleration helper via JAX autodiff
|
|
73
|
+
|
|
74
|
+
## Installation
|
|
75
|
+
|
|
76
|
+
Install from source:
|
|
77
|
+
|
|
78
|
+
```bash
|
|
79
|
+
pip install -e .
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
`yggdrax` is not on PyPI yet. Install it from GitHub first (use the latest
|
|
83
|
+
`main`, which includes native `RadixTree` JAX pytree registration):
|
|
84
|
+
|
|
85
|
+
```bash
|
|
86
|
+
git clone https://github.com/TobiBu/yggdrax.git
|
|
87
|
+
cd yggdrax
|
|
88
|
+
pip install -e .
|
|
89
|
+
cd ..
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
Install with development tooling:
|
|
93
|
+
|
|
94
|
+
```bash
|
|
95
|
+
pip install -e ".[dev]"
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
## Quick Start
|
|
99
|
+
|
|
100
|
+
```python
|
|
101
|
+
import jax
|
|
102
|
+
import jax.numpy as jnp
|
|
103
|
+
|
|
104
|
+
from jaccpot import FastMultipoleMethod
|
|
105
|
+
|
|
106
|
+
key = jax.random.PRNGKey(0)
|
|
107
|
+
key_pos, key_mass = jax.random.split(key)
|
|
108
|
+
positions = jax.random.uniform(key_pos, (1024, 3), minval=-1.0, maxval=1.0)
|
|
109
|
+
masses = jax.random.uniform(key_mass, (1024,), minval=0.5, maxval=1.5)
|
|
110
|
+
|
|
111
|
+
solver = FastMultipoleMethod(preset="balanced", basis="solidfmm")
|
|
112
|
+
accelerations = solver.compute_accelerations(positions, masses)
|
|
113
|
+
print(accelerations.shape)
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
Real-basis rotate+scale FMM uses the same high-level API:
|
|
117
|
+
|
|
118
|
+
```python
|
|
119
|
+
solver = FastMultipoleMethod(
|
|
120
|
+
preset="accurate",
|
|
121
|
+
basis="real",
|
|
122
|
+
adaptive_order=True,
|
|
123
|
+
p_gears=(2, 3, 4),
|
|
124
|
+
)
|
|
125
|
+
accelerations = solver.compute_accelerations(positions, masses, max_order=4)
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
For very large single-GPU runs, use the dedicated throughput/memory preset:
|
|
129
|
+
|
|
130
|
+
```python
|
|
131
|
+
solver = FastMultipoleMethod(
|
|
132
|
+
preset="large_n_gpu",
|
|
133
|
+
basis="solidfmm",
|
|
134
|
+
precision="fp32",
|
|
135
|
+
)
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
`large_n_gpu` keeps JIT enabled while applying memory-oriented runtime defaults
|
|
139
|
+
for large particle-count single-GPU runs (streamed far-pair mode, reduced
|
|
140
|
+
near-field precompute retention, and cache retention disabled).
|
|
141
|
+
|
|
142
|
+
For split-step integrators (for example active-particle substeps), you can
|
|
143
|
+
evaluate only a subset while still using all particles as FMM sources:
|
|
144
|
+
|
|
145
|
+
```python
|
|
146
|
+
active = jnp.asarray([0, 7, 11, 32], dtype=jnp.int32)
|
|
147
|
+
state = solver.prepare_state(positions, masses)
|
|
148
|
+
active_acc = solver.evaluate_prepared_state(state, target_indices=active)
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
For integrators that require jerk, use:
|
|
152
|
+
|
|
153
|
+
```python
|
|
154
|
+
velocities = jax.random.uniform(key_pos, (1024, 3), minval=-0.2, maxval=0.2)
|
|
155
|
+
acc, jerk = solver.compute_accelerations_and_jerk(
|
|
156
|
+
positions,
|
|
157
|
+
masses,
|
|
158
|
+
velocities,
|
|
159
|
+
jerk_mode="fast_approx", # or "accurate"
|
|
160
|
+
)
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
See [docs/derivatives_and_jerk.md](docs/derivatives_and_jerk.md) for API details,
|
|
164
|
+
mode tradeoffs, and output tensor layouts.
|
|
165
|
+
Current higher-order status:
|
|
166
|
+
|
|
167
|
+
- jerk is available via `compute_accelerations_and_jerk(...)`
|
|
168
|
+
- higher total time derivatives are available via
|
|
169
|
+
`compute_accelerations_with_time_derivatives(...)`
|
|
170
|
+
- public time-derivative support currently reaches crackle
|
|
171
|
+
(`max_time_derivative_order=3`)
|
|
172
|
+
- the general time-derivative API currently uses the analytic `accurate` path
|
|
173
|
+
- orders above crackle are not yet available
|
|
174
|
+
- acceleration spatial derivatives (`max_acc_derivative_order > 0`) currently
|
|
175
|
+
require `basis="solidfmm"`
|
|
176
|
+
|
|
177
|
+
There is also a worked example notebook for jerk, snap, and crackle, including
|
|
178
|
+
a small-`N` direct-sum accuracy check:
|
|
179
|
+
[`examples/time_derivatives_demo.ipynb`](/Users/buck/Documents/Nexus/Projects/jaccpot/examples/time_derivatives_demo.ipynb).
|
|
180
|
+
|
|
181
|
+
### Jerk Mode Guide
|
|
182
|
+
|
|
183
|
+
| Goal | Mode | Notes |
|
|
184
|
+
|---|---|---|
|
|
185
|
+
| Lowest runtime overhead | `fast_approx` | Exact near-field jerk + far-field convective term. |
|
|
186
|
+
| Highest fidelity (includes source-motion effects) | `accurate` | Analytic far-field source-motion term + convective + exact near-field jerk. |
|
|
187
|
+
| Stable default for large production runs | `fast_approx` | Benchmark against your own workload before switching defaults. |
|
|
188
|
+
|
|
189
|
+
For ODISSEO-style primitive states `(N, 2, 3)`, you can use the adapter:
|
|
190
|
+
|
|
191
|
+
```python
|
|
192
|
+
from jaccpot import OdisseoFMMCoupler
|
|
193
|
+
|
|
194
|
+
coupler = OdisseoFMMCoupler(solver, leaf_size=16, max_order=4)
|
|
195
|
+
coupler.prepare(primitive_state, masses) # full source tree
|
|
196
|
+
acc_active = coupler.accelerations(primitive_state, active_indices=active)
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
## Octree Backend
|
|
200
|
+
|
|
201
|
+
The default runtime path remains radix-oriented. To request explicit octree
|
|
202
|
+
execution, configure both the tree type and runtime backend:
|
|
203
|
+
|
|
204
|
+
```python
|
|
205
|
+
from jaccpot import (
|
|
206
|
+
FastMultipoleMethod,
|
|
207
|
+
FMMAdvancedConfig,
|
|
208
|
+
RuntimePolicyConfig,
|
|
209
|
+
TreeConfig,
|
|
210
|
+
)
|
|
211
|
+
|
|
212
|
+
solver = FastMultipoleMethod(
|
|
213
|
+
preset="fast",
|
|
214
|
+
basis="solidfmm",
|
|
215
|
+
advanced=FMMAdvancedConfig(
|
|
216
|
+
tree=TreeConfig(tree_type="octree"),
|
|
217
|
+
runtime=RuntimePolicyConfig(execution_backend="octree"),
|
|
218
|
+
),
|
|
219
|
+
)
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
Current practical scope:
|
|
223
|
+
|
|
224
|
+
- the octree backend is validated for `basis="solidfmm"`
|
|
225
|
+
- prepared-state evaluation supports full outputs, target subsets, potentials,
|
|
226
|
+
JIT/eager traversal, and prepared-state cache reuse
|
|
227
|
+
- non-default runtime modes such as baseline nearfield and class-major
|
|
228
|
+
farfield are covered in the solver tests
|
|
229
|
+
|
|
230
|
+
Still worth keeping in mind:
|
|
231
|
+
|
|
232
|
+
- `execution_backend="auto"` may still resolve to the radix backend
|
|
233
|
+
- topology reuse remains radix-only
|
|
234
|
+
- validation is currently most reliable on the preferred project validation GPU
|
|
235
|
+
|
|
236
|
+
Example:
|
|
237
|
+
|
|
238
|
+
- [examples/compare_yggdrax_jaccpot_prepare.py](examples/compare_yggdrax_jaccpot_prepare.py)
|
|
239
|
+
|
|
240
|
+
## Basis Selection
|
|
241
|
+
|
|
242
|
+
- `basis="complex"` or `basis="solidfmm"`:
|
|
243
|
+
default complex solidFMM-compatible path
|
|
244
|
+
- `basis="real"`:
|
|
245
|
+
real spherical harmonic coefficient layout with rotate+scale-to-z M2L
|
|
246
|
+
- `basis="cartesian"`:
|
|
247
|
+
cartesian multipole/local expansion path
|
|
248
|
+
|
|
249
|
+
The default remains the existing complex solidFMM-compatible path.
|
|
250
|
+
|
|
251
|
+
## Precision Control
|
|
252
|
+
|
|
253
|
+
Use `precision` to select runtime dtype explicitly:
|
|
254
|
+
|
|
255
|
+
```python
|
|
256
|
+
solver_fp32 = FastMultipoleMethod(
|
|
257
|
+
preset="fast",
|
|
258
|
+
basis="solidfmm",
|
|
259
|
+
precision="fp32",
|
|
260
|
+
)
|
|
261
|
+
|
|
262
|
+
solver_fp64 = FastMultipoleMethod(
|
|
263
|
+
preset="accurate",
|
|
264
|
+
basis="solidfmm",
|
|
265
|
+
precision="fp64",
|
|
266
|
+
)
|
|
267
|
+
```
|
|
268
|
+
|
|
269
|
+
`precision="fp64"` requires `jax_enable_x64=True`. You can still pass
|
|
270
|
+
`working_dtype` directly; if both are set, they must match.
|
|
271
|
+
|
|
272
|
+
## Adaptive Order
|
|
273
|
+
|
|
274
|
+
Use `adaptive_order=True` together with a static gear list:
|
|
275
|
+
|
|
276
|
+
```python
|
|
277
|
+
solver = FastMultipoleMethod(
|
|
278
|
+
preset="accurate",
|
|
279
|
+
basis="real",
|
|
280
|
+
adaptive_order=True,
|
|
281
|
+
p_gears=(2, 3, 4),
|
|
282
|
+
)
|
|
283
|
+
```
|
|
284
|
+
|
|
285
|
+
`p_gears` must be a fixed tuple or list of orders. This keeps all hot paths
|
|
286
|
+
JIT-friendly and avoids shape polymorphism.
|
|
287
|
+
|
|
288
|
+
Adaptive order selection now uses yggdrax's generic `pair_policy` +
|
|
289
|
+
`interaction_tags` traversal hook. The tree backend only provides generic
|
|
290
|
+
far-pair tags; jaccpot owns the solver-side policy state, order selection, and
|
|
291
|
+
per-order bucketing.
|
|
292
|
+
|
|
293
|
+
The current adaptive acceptance model is solver-owned and error-aware:
|
|
294
|
+
- acceptance uses the highest available order as a Dehnen-style safety check
|
|
295
|
+
- accepted pairs are limited to a relaxed geometric cone to avoid pathological
|
|
296
|
+
over-acceptance
|
|
297
|
+
- once accepted, the solver picks the smallest passing order from `p_gears`
|
|
298
|
+
|
|
299
|
+
In other words, the highest candidate order decides whether a pair is safe to
|
|
300
|
+
accept, while the first passing order decides how much far-field work is needed.
|
|
301
|
+
The current notebook example prints the resulting tag-derived
|
|
302
|
+
`far_pairs_by_gear_counts` from the solver runtime.
|
|
303
|
+
|
|
304
|
+
Adaptive traversal currently has two practical runtime modes:
|
|
305
|
+
|
|
306
|
+
- `adaptive_error_model="tail_proxy"` (default):
|
|
307
|
+
the validated high-performance mode; this remains the recommended default
|
|
308
|
+
when runtime matters most
|
|
309
|
+
- `adaptive_error_model="dehnen_paper"`:
|
|
310
|
+
the paper-inspired comparison mode; for JAX-native runs pair it with
|
|
311
|
+
`dehnen_geometry_mode="tree_approx"`
|
|
312
|
+
|
|
313
|
+
Other available knobs:
|
|
314
|
+
|
|
315
|
+
- `adaptive_error_model="dehnen_degree"`:
|
|
316
|
+
a simplified degree-resolved Dehnen-style source-power estimator
|
|
317
|
+
- `dehnen_geometry_mode="exact"`:
|
|
318
|
+
exact reference geometry for paper comparisons; not a throughput mode
|
|
319
|
+
- `dehnen_geometry_mode="tree_approx"`:
|
|
320
|
+
JAX-native paper geometry based on approximate leaf spheres plus upward
|
|
321
|
+
merged spheres
|
|
322
|
+
- `adaptive_eps=...`:
|
|
323
|
+
override the default theta-derived adaptive tolerance with a direct
|
|
324
|
+
solver-side tolerance scale
|
|
325
|
+
|
|
326
|
+
Examples:
|
|
327
|
+
|
|
328
|
+
- [examples/real_sh_adaptive_order.ipynb](/Users/buck/Documents/Nexus/Projects/jaccpot/examples/real_sh_adaptive_order.ipynb)
|
|
329
|
+
- [examples/real_sh_rot_scale_demo.py](/Users/buck/Documents/Nexus/Projects/jaccpot/examples/real_sh_rot_scale_demo.py)
|
|
330
|
+
|
|
331
|
+
## Reproducible Comparison Modes
|
|
332
|
+
|
|
333
|
+
Use the following three solver configurations for reproducible comparisons on this branch.
|
|
334
|
+
Keep the remaining benchmark settings fixed, for example:
|
|
335
|
+
|
|
336
|
+
- `preset="accurate"`
|
|
337
|
+
- `basis="real"`
|
|
338
|
+
- `theta=0.6`
|
|
339
|
+
- `leaf_size=16`
|
|
340
|
+
- `max_order=4`
|
|
341
|
+
- `p_gears=(2, 3, 4)` for adaptive runs
|
|
342
|
+
- enlarged traversal caps, as used in `examples/adaptive_vs_fixed_benchmark.ipynb`
|
|
343
|
+
|
|
344
|
+
Fixed non-adaptive baseline:
|
|
345
|
+
|
|
346
|
+
```python
|
|
347
|
+
fixed = FastMultipoleMethod(
|
|
348
|
+
preset="accurate",
|
|
349
|
+
basis="real",
|
|
350
|
+
theta=0.6,
|
|
351
|
+
adaptive_order=False,
|
|
352
|
+
)
|
|
353
|
+
```
|
|
354
|
+
|
|
355
|
+
Adaptive high-performance default:
|
|
356
|
+
|
|
357
|
+
```python
|
|
358
|
+
tail_proxy = FastMultipoleMethod(
|
|
359
|
+
preset="accurate",
|
|
360
|
+
basis="real",
|
|
361
|
+
theta=0.6,
|
|
362
|
+
adaptive_order=True,
|
|
363
|
+
p_gears=(2, 3, 4),
|
|
364
|
+
adaptive_error_model="tail_proxy",
|
|
365
|
+
mac_force_scale_mode="prev",
|
|
366
|
+
)
|
|
367
|
+
```
|
|
368
|
+
|
|
369
|
+
Adaptive paper-inspired JAX-native mode:
|
|
370
|
+
|
|
371
|
+
```python
|
|
372
|
+
dehnen_paper = FastMultipoleMethod(
|
|
373
|
+
preset="accurate",
|
|
374
|
+
basis="real",
|
|
375
|
+
theta=0.6,
|
|
376
|
+
adaptive_order=True,
|
|
377
|
+
p_gears=(2, 3, 4),
|
|
378
|
+
adaptive_error_model="dehnen_paper",
|
|
379
|
+
adaptive_eps=1.0e-3,
|
|
380
|
+
dehnen_geometry_mode="tree_approx",
|
|
381
|
+
mac_force_scale_mode="paper",
|
|
382
|
+
)
|
|
383
|
+
```
|
|
384
|
+
|
|
385
|
+
Interpretation on the current branch:
|
|
386
|
+
|
|
387
|
+
- `fixed`: fastest non-adaptive baseline
|
|
388
|
+
- `tail_proxy`: best validated adaptive runtime default
|
|
389
|
+
- `dehnen_paper`: higher-accuracy, paper-inspired comparison mode
|
|
390
|
+
|
|
391
|
+
## Force Scale Modes For Adaptive Traversal
|
|
392
|
+
|
|
393
|
+
Adaptive traversal can weight its solver-side policy state with per-node force
|
|
394
|
+
scales. Select how those scales are estimated with `mac_force_scale_mode`:
|
|
395
|
+
|
|
396
|
+
- `"prev"`:
|
|
397
|
+
reuse the previous full-step per-node force-scale estimate (`self._last_force_scale_nodes`).
|
|
398
|
+
This is the cheapest option and is the practical default for `tail_proxy`.
|
|
399
|
+
- `"prepass"`:
|
|
400
|
+
run a cheap lowest-order prepass for the current configuration and derive force
|
|
401
|
+
scales from that pass.
|
|
402
|
+
- `"paper"`:
|
|
403
|
+
run the stricter paper-style current-step prepass used by
|
|
404
|
+
`adaptive_error_model="dehnen_paper"`.
|
|
405
|
+
|
|
406
|
+
Interpretation:
|
|
407
|
+
|
|
408
|
+
- `prev` is a runtime-oriented reuse mode.
|
|
409
|
+
- `paper` is the more publication/reference-oriented mode because it derives the
|
|
410
|
+
threshold from a dedicated current-step prepass rather than from historical state.
|
|
411
|
+
- `prepass` sits between the two as a generic current-step estimate that is not
|
|
412
|
+
specifically tied to the paper-style Dehnen path.
|
|
413
|
+
|
|
414
|
+
These scales stay inside jaccpot's adaptive policy state; they are no longer
|
|
415
|
+
exported as backend-specific traversal `node_features`.
|
|
416
|
+
|
|
417
|
+
## Pallas Acceleration
|
|
418
|
+
|
|
419
|
+
The real-basis z-translation core can be accelerated with Pallas:
|
|
420
|
+
|
|
421
|
+
```python
|
|
422
|
+
solver = FastMultipoleMethod(
|
|
423
|
+
preset="accurate",
|
|
424
|
+
basis="real",
|
|
425
|
+
use_pallas=True,
|
|
426
|
+
)
|
|
427
|
+
```
|
|
428
|
+
|
|
429
|
+
Current behavior:
|
|
430
|
+
|
|
431
|
+
- rotations stay in pure JAX
|
|
432
|
+
- only the real-basis z-axis M2L core is offloaded
|
|
433
|
+
- unsupported backends fall back to the pure-JAX kernel automatically
|
|
434
|
+
|
|
435
|
+
On the current `expanse` CPU environment, the example reports fallback rather
|
|
436
|
+
than true Pallas execution:
|
|
437
|
+
|
|
438
|
+
- [examples/pallas_m2l_speed.py](/Users/buck/Documents/Nexus/Projects/jaccpot/examples/pallas_m2l_speed.py)
|
|
439
|
+
|
|
440
|
+
## Topology Reuse
|
|
441
|
+
|
|
442
|
+
For small multi-step particle motion, you can reuse cached topology and
|
|
443
|
+
interaction lists for a bounded number of steps:
|
|
444
|
+
|
|
445
|
+
```python
|
|
446
|
+
solver = FastMultipoleMethod(
|
|
447
|
+
preset="accurate",
|
|
448
|
+
basis="real",
|
|
449
|
+
reuse_topology=True,
|
|
450
|
+
rebuild_every=3,
|
|
451
|
+
)
|
|
452
|
+
```
|
|
453
|
+
|
|
454
|
+
The solver always recomputes reordered particles, geometry, upward multipoles,
|
|
455
|
+
and downward locals for the current state. Reuse only applies to cached
|
|
456
|
+
topology/traversal artifacts when the Morton ordering key remains unchanged.
|
|
457
|
+
|
|
458
|
+
Example:
|
|
459
|
+
|
|
460
|
+
- [examples/reuse_topology_demo.py](/Users/buck/Documents/Nexus/Projects/jaccpot/examples/reuse_topology_demo.py)
|
|
461
|
+
|
|
462
|
+
## Development
|
|
463
|
+
|
|
464
|
+
Run quality gates locally:
|
|
465
|
+
|
|
466
|
+
```bash
|
|
467
|
+
black --check .
|
|
468
|
+
isort --check-only .
|
|
469
|
+
pytest
|
|
470
|
+
```
|
|
471
|
+
|
|
472
|
+
Or run pre-commit hooks:
|
|
473
|
+
|
|
474
|
+
```bash
|
|
475
|
+
pre-commit run --all-files
|
|
476
|
+
```
|
|
477
|
+
|
|
478
|
+
Coverage is enforced in CI via `pytest-cov`:
|
|
479
|
+
|
|
480
|
+
```bash
|
|
481
|
+
pytest --cov=jaccpot --cov-report=term-missing
|
|
482
|
+
```
|
|
483
|
+
|
|
484
|
+
### Performance Guard
|
|
485
|
+
|
|
486
|
+
CI also runs a benchmark regression guard based on:
|
|
487
|
+
|
|
488
|
+
- [bench/bench_parallel_paths.py](/Users/buck/Documents/Nexus/Projects/jaccpot/bench/bench_parallel_paths.py)
|
|
489
|
+
- [bench/ci_benchmark_guard.py](/Users/buck/Documents/Nexus/Projects/jaccpot/bench/ci_benchmark_guard.py)
|
|
490
|
+
- [bench/benchmark_baseline.json](/Users/buck/Documents/Nexus/Projects/jaccpot/bench/benchmark_baseline.json)
|
|
491
|
+
|
|
492
|
+
Run the lightweight runtime-path benchmark and CI guard locally:
|
|
493
|
+
|
|
494
|
+
```bash
|
|
495
|
+
python -m bench.bench_parallel_paths --n 512 --runs 3 --warmup 1
|
|
496
|
+
python -m bench.ci_benchmark_guard --n 384 --runs 2 --warmup 1
|
|
497
|
+
```
|
|
498
|
+
|
|
499
|
+
If a performance change is intentional, refresh the baseline:
|
|
500
|
+
|
|
501
|
+
1. Run `bench/bench_parallel_paths.py` with the CI benchmark arguments.
|
|
502
|
+
2. Read the `timings_s` line and update:
|
|
503
|
+
`target_eval_mean_s` and `adaptive_prepare_mean_s` in
|
|
504
|
+
`bench/benchmark_baseline.json`.
|
|
505
|
+
3. Re-run `bench/ci_benchmark_guard.py` to confirm the new baseline passes.
|
|
506
|
+
|
|
507
|
+
## Examples
|
|
508
|
+
|
|
509
|
+
- `examples/benchmark_runtime_accuracy.ipynb`: main runtime/accuracy benchmark workflow
|
|
510
|
+
- `examples/adaptive_vs_fixed_benchmark.ipynb`: adaptive-order vs fixed-order comparison
|
|
511
|
+
- `examples/benchmark_gpu_radix_runtime.ipynb`: GPU/radix runtime and memory-pressure deep dive
|
|
512
|
+
- `examples/benchmark_gpu_single_n_memory.ipynb`: interactive single-`N` GPU memory probe with plots/tables
|
|
513
|
+
- `examples/benchmark_gpu_n_ladder_production.py`: production-oriented large-`N` parameter sweep
|
|
514
|
+
- `examples/time_derivatives_demo.ipynb`: usage plus direct-sum accuracy checks for jerk, snap, and crackle
|
|
515
|
+
- `examples/jerk_modes_demo.ipynb`: compare jerk `fast_approx` vs `accurate`, including analytic source-motion behavior
|
|
516
|
+
- `examples/real_sh_adaptive_order.ipynb`: real-basis adaptive-order demo
|
|
517
|
+
|
|
518
|
+
## Runtime Type Checking
|
|
519
|
+
|
|
520
|
+
`jaccpot` can enable package-wide runtime checking for annotated callables using
|
|
521
|
+
`jaxtyping` + `beartype` at import time.
|
|
522
|
+
|
|
523
|
+
- Disabled by default.
|
|
524
|
+
- Enable when needed with:
|
|
525
|
+
|
|
526
|
+
```bash
|
|
527
|
+
export JACCPOT_RUNTIME_TYPECHECK=1
|
|
528
|
+
```
|
|
529
|
+
|
|
530
|
+
## Project Structure
|
|
531
|
+
|
|
532
|
+
- `jaccpot/solver.py`: preset-first user-facing FMM API
|
|
533
|
+
- `jaccpot/config.py`: config model for solver/runtime knobs
|
|
534
|
+
- `jaccpot/runtime`: execution internals and integration with yggdrax artifacts
|
|
535
|
+
- `jaccpot/operators`: harmonic, translation, and multipole operators
|
|
536
|
+
- `jaccpot/upward`, `jaccpot/downward`, `jaccpot/nearfield`: sweep and near-field modules
|
|
537
|
+
- `tests`: unit, integration, and performance checks
|
|
538
|
+
|
|
539
|
+
## CI
|
|
540
|
+
|
|
541
|
+
GitHub Actions runs:
|
|
542
|
+
|
|
543
|
+
- formatter checks (`black`, `isort`)
|
|
544
|
+
- unit/integration tests with coverage threshold
|
|
545
|
+
- release build and PyPI publish on version tags
|
|
546
|
+
|
|
547
|
+
Workflow files:
|
|
548
|
+
|
|
549
|
+
- `.github/workflows/ci.yml`
|
|
550
|
+
- `.github/workflows/release.yml`
|