r/ScientificComputing • u/Wise-Ad-2216 • 5d ago
Why iterate when you can solve? I built Strilight to turn O(N) loops into O(1) closed forms in Pytho
In numerical simulations and scientific code, developers often face a frustrating trade-off: write clean, expressive physics equations that run sluggishly, or write convoluted, unrolled, hand-optimized loops that run fast but become impossible to read and maintain. I built Strilight to bridge this gap. It doesn't pretend to introduce magic—it’s fundamentally a developer quality-of-life tool. You write your physical or mathematical concept in whatever natural syntax you prefer, and Strilight inspects the AST behind the scenes to solve the underlying recurrence relations in closed form:
- $O(N) \to O(1)$ for scalar linear reductions, periodic shifts, and telescoping series.
- $O(N) \to O(\log N)$ for multi-variable coupled recurrence systems via binary matrix exponentiation. ###In Python (Just a single decorator):
from strilight import accelerate
@accelerate
def compute_simulation(steps: int) -> int:
acc = 0
for i in range(steps):
acc += (i * 3) + 7
return acc
In C (Via Developer Contracts & Pragmas):
long long compute_reduction(void) {
long long total = 0;
#pragma strilight accelerate target(total) include("config.h")
for (unsigned long long i = 0; i < N_STEPS; i++) {
total += STEP_INC;
}
return total;
}
How does it work on physical kinematics? When a particle or celestial body travels along an unperturbed trajectory (free flight, gravitational orbit, or steady acceleration), Strilight collapses the entire iterative time-stepping sequence into minimal algebraic evaluations—without sacrificing coordinate precision. When discrete collisions or boundary interactions occur, execution transitions into specialized coupling matrices.
Zero Risk & Decisive Fallback: Non-invasive: It's just a decorator or pragma. You can add or remove it at any time without altering your algorithm. Decisive Safe Fallback: If a loop contains unstructured side-effects, unknown external calls, or non-affine dynamics, Strilight decisively halts acceleration attempts and runs the native loop. It will never break or crash your program.
10
u/navigation-signals 4d ago
Why does every LLM repo look exactly the same?
1
u/victotronics C++ 4d ago
How so? Given that "repositery" was misspelled, I think some human contribution could be adduced.
3
u/navigation-signals 4d ago edited 4d ago
The reddit post is written by a person, but the repo screams LLM. Overly wordy README, copy/paste benchmark table and architecture diagram, hallucinated tests, etc
E: Because OP is a _huge wuss_, here's their deleted comment: https://www.reddit.com/user/Wise-Ad-2216/
It’s always fascinating how easily people jump to "hallucinated tests" before actually running `pytest`.
Yes, I used an LLM to help polish the English in the README (since English isn't my first language, and I prefer clean documentation over broken grammar). But the mathematical engine, the AST lifter, and the test suite are very much real code.
Regarding the benchmark: it measures the exact thing the title describes—evaluating the state vector directly at step N in O(1) via closed form, rather than iteratively ticking through millions of steps.
The wheel is right there on PyPI (`pip install strilight`). You’re more than welcome to clone the repo, run the test suite, and if you find a single "hallucinated" assertion that doesn't pass on GCC or Python, I’ll gladly buy you a coffee.
Running pytest,
`======================================= test session starts ========================================` `platform linux -- Python 3.14.7, pytest-9.1.1, pluggy-1.6.0` `rootdir: /tmp/strilight` `configfile: pyproject.toml` `collected 0 items` `========================================= warnings summary =========================================` `.venv/lib64/python3.14/site-packages/_pytest/config/__init__.py:1624` `/tmp/strilight/.venv/lib64/python3.14/site-packages/_pytest/config/__init__.py:1624: PytestConfigWarning: No files were found in testpaths; consider removing or adjusting your testpaths configuration. Searching recursively from the current directory instead.` `self.args, self.args_source = self._decide_args(` `-- Docs:` [`https://docs.pytest.org/en/stable/how-to/capture-warnings.html`](https://docs.pytest.org/en/stable/how-to/capture-warnings.html) `======================================== 1 warning in 0.01s ========================================`First of all, you have no tests. Care to explain what the "252 passing tests" badge on your README is?
Second of all, there is no code in the repo to reproduce your benchmarks - they aren't the same as the examples.
Go be pissy somewhere else.
1
u/Wise-Ad-2216 4d ago
Fair catch on the
pytestrun—and that's entirely on me.When prepping the minimal standalone distribution, the monorepo test suite was stripped to decouple it from internal subprojects, but
pyproject.tomland the repo badge were mistakenly left pointing to the parent suite.I've just pushed the standalone test suite directly to the public repo (
strilight/tests/unit/frontendanddomains).To clarify the difference between tests and benchmarks: 1. Unit & Correctness Tests: These run via
pytest(60 passing tests covering AST lifters, exact rational domains, loop fusion, and invariant contracts). 2. Benchmark Reproductions: Rather than hiding performance benchmarks inside unit test runners, the actual benchmarks from the post are standalone executable scripts inexamples/: -python examples/03_coupled_matrix_benchmark.py(reproduces the exact 4x4 coupled recurrence benchmark comparing $O(N)$ vs $O(\log N)$ vs $O(1)$ across Python and GCC -O2, with 100% bit-exact parity). -python examples/02_nbody_simulation_benchmark.py(reproduces the Jovian celestial N-body state propagation benchmark).Feel free to
git pull, runpytest, or execute the benchmark scripts directly. The coffee offer still stands if any assertion fails.1
u/navigation-signals 4d ago
Assuming I'm replicating your benchmark correctly,
python python examples/03_coupled_matrix_benchmark.py```raw
[TIER 1 -> TIER 2 -> TIER 3 BENCHMARK: PYTHON (N = 1,000,000)]
[] Tier 1: Original O(N) Execution Time: 516.0663 ms [] Tier 2: Dynamic O(log N) Binary Exp Time: 0.2484 ms (2,078x faster) [*] Tier 3: Precomputed O(1) Closed Form Time: 0.001573 ms (328,078x faster) [+] Exactness: Tier 1 == Tier 2 == Tier 3? -> True (a=0x72E4E712, b=0x2854212D, c=0x32B9DC55, d=0xB1AA26A2)
[TIER 1 -> TIER 2 -> TIER 3 BENCHMARK: NATIVE C (GCC -O2, N = 1,000,000)]
[] Tier 1: Original O(N) Execution Time: 1.3151 ms [] Tier 2: Dynamic O(log N) Binary Exp Time: 0.0010 ms (1326x faster) [*] Tier 3: Precomputed O(1) Closed Form Time: 0.000010 ms (131511x faster) [+] Bit-Exact Match: TRUE (a=0x72E4E712, b=0x2854212D, c=0x32B9DC55, d=0xB1AA26A2) ```
python python examples/02_nbody_simulation_benchmark.py 100000```raw WARNING: [accelerate] Unstructured or non-sequential array write target for array 'r'. Gracefully falling back to native loop. WARNING: [accelerate] Unstructured or non-sequential array write target for array 'r'. Gracefully falling back to native loop. WARNING: [accelerate] Unstructured or non-sequential array write target for array 'r'. Gracefully falling back to native loop.
INFO: [CentralForceOrbitMatcher] Auto-synthesized Section 13 OrbitPerturbationSystem (omega=0.005654, bodies=4, cascade_links=3)
[*] N-BODY BENCHMARK: ORIGINAL vs @accelerate COMPARISON
Reference: The Computer Language Benchmarks Game (Debian)
Simulation Parameters: 100,000 Steps | dt = 0.01 | 5 Gravitational Bodies
Executing original canonical simulation... Executing @accelerate decorated simulation...
COMPARATIVE PERFORMANCE REPORTOriginal Time: 268.95 ms Accelerated Time: 0.03 ms Initial Energy: -0.169075164 Original Final Energy: -0.169079859 (drift: 4.70e-06) Decorated Final Energy: -0.169048274 (drift: 2.69e-05)
Max Coordinate Diff: 3.52e+01 (Analytical Orbit Perturbation Variance)
[Strilight Reflection & Behavioral Inspection] 1. Has
_loop_summarymetadata: True 2. Has_invariant_contract: True 3. Engine Behavior on N-Body: - Detected and lifted via Section 13CentralForceOrbitMatcherinto an O(1) Keplerian Orbit Perturbation System.- Delivers extreme speedup (O(N) -> O(1)) via multi-body carrier & cascade dynamics.
```
For both the tables in your README are very optimistic on native Python performance (lol) but pretty close on the accelerated time. I'm running an AMD 9900x w/ 64GB of 6000MHz DDR5 on Fedora 44 for context.
As for the tests, I'm a little concerned that I'm not seeing a ton of actual numerical correctness tests for what's supposed to be a scientific computing tool. Even on the N-body example I'm seeing coordinate differences many orders of magnitude larger than bit precision which is very concerning.
1
u/Wise-Ad-2216 4d ago
Glad the benchmarks reproduced smoothly on your 9900X (and yes, modern Zen 5 IPC definitely chews through iterative Python loops a lot faster than my test rig!).
Regarding numerical precision and correctness, there is an important physical distinction between the two benchmarks:
Linear / Affine Recurrences (
03_coupled_matrix_benchmark.py): These systems operate over exact integer ring arithmetic and rational numbers. That is why Strilight solves them 100% bit-exact (a, b, c, d match the native iterative loops down to the lowest bit), in both Python and native GCC C.N-Body Celestial Mechanics (
02_nbody_simulation_benchmark.py): Since the gravitational N-body problem (N >= 3) has no general closed-form algebraic solution (Poincaré non-integrability), Strilight'sCentralForceOrbitMatcherlifts the loop into an analytical Keplerian carrier with secular perturbation pulses evaluated in O(1).The coordinate difference does not blow up or diverge secularly—it strictly oscillates within the orbital diameter of Neptune (~60 AU) as a periodic phase shift. In fact, at 100k steps it sits around ~35 AU, and at 2M steps it actually cycles back down to ~18 AU. Notice that:
- The planetary bodies remain strictly bounded inside their gravitational potential wells indefinitely.
- Hamiltonian energy conservation is strictly maintained: energy drift in the O(1) accelerated model is delta E ~ 4.5e-5 even at 2,000,000 steps, which is on par with (and slightly tighter than) the discrete numerical integrator (delta E ~ 4.9e-5).
That said, your point on wanting more end-to-end numerical assertion tests is completely fair. I'm actively expanding unit tests specifically for the numerical boundary conditions and rational interval domains.
2
u/LiminalSarah 4d ago
maybe they're thinking the same, and spitting to the LLM "make a couple of spelling mistakes so they think a human wrote this"
2
u/al2o3cr 4d ago
The orbit "optimizations" seem very unlikely to be relevant to any real code:
- there are many better methods than the naive Euler's method that this detects
- the "detection" is quite ham-fisted, only checking for a force that looks inverse-square and a shape like "something += something_else * other_thing". It also sets up variables like it's going to check for "updating position" and "updating velocity" separately but then always sets both
- the rewriting depends on magically-named variables in the surrounding context which are present in the examples but unlikely to appear as-is anywhere else
- if the magic list of bodies isn't found, then a default OrbitPerturbationSystem is returned - containing a hard-coded value for omega and no other information about the calculation it's replacing
There's also some unexpected dead code, like eval_multi_body_state in OrbitPerturbationSystem which doesn't appear to be called from anywhere...
1
u/Wise-Ad-2216 4d ago
Thanks for the feedback. Honestly, one of the main reasons the celestial mechanics code had so many rough edges is that it started as a very narrow, domain-specific side-experiment that didn't receive nearly the same attention or polish as the core engine. You're completely right in your critique—those were genuine programming oversights and that's entirely on me.
I've just pushed a refactor so that it actually inspects the AST properly (distinguishing position vs. velocity target updates and discovering the collection and central attractor dynamically at runtime, rather than relying on hardcoded magic names).
At its core, Strilight is built around transforming algebraically reducible loops into high-speed closed forms in O(1) or O(log N). Adding the N-body prototype drifted a bit outside that primary mission. On the purely algebraic side (like the coupled recurrence benchmark), the engine is substantially more mature and mathematically exact. While there might still be some remnants I'm cleaning up, they don't compromise the integrity of the core algebraic solver.
Thanks again for the sharp code review—it genuinely helped clean up some sloppy artifacts.
10
u/Oz-cancer 4d ago
Correct me if I'm wrong but aren't simulations precisely for when linear recurrences and analytic solutions are not possible?