Thanks for the advice; you're absolutely right ,it was just to grab attention.
HN user
sade_95
Thanks for the epicycles clarification, I did not know each body had its own fitted set. This makes the parallel with overfitting even stronger.
And I think we agree on the real problem: a good fit on one problem, made with a universal approximator, is not an explanation. It can be just a compressed description, maybe with a cause behind, maybe not. I cannot fix this, but I added this week a small thing to at least see it: a bootstrap stability check. You resample the data, refit, and look how often the same form comes back. High frequency does not prove anything causal. But low frequency is a good signal that you are only looking at a Fourier-type fit, not a law. The decision stays with the user, the tool only makes it visible.
This is exactly the niche I had in mind, thank you for the concrete example. Where theory works (small deltas, linear regime) nobody needs my tool. Where turbulence starts, engineers already live with empirical correlations and weird exponents since one century, Nusselt-Reynolds style. A GP search is just a systematic way to produce this kind of correlation, with a Pareto front instead of one formula.
And your point about units is the industrial reality: the convention "measure in these units, then use the numbers" is exactly what the tool does internally. A proper dimensional mode stays on my roadmap, but I feel less bad about it now.
You are right, and I concede the point. I chose the operators and the parsimony rule knowing already that laws like Kepler exist. So "Kepler took 10 years" is a bit of theater, the honest claim is only: if a short law exists in this basis, the search finds it fast. Kepler had to invent the idea that such law exists at all, and fight Tycho's data with no computer. Not the same job.
For the epicycles, for me this is exactly the argument for parsimony pressure. Epicycles fit better because you can always add one more circle, same as adding parameters in a NN. They lose on description length, and that is the only defense I have too. And it is not perfect: on noisy data my tool produces its own epicycles, formulas that fit very well and mean nothing. One battery model it gave me predicted the battery heals itself after cycle 264. Great fit, zero physics. So yes, a conjecture machine that a domain expert must verify. No more than that.
Funny you ask, I actually spent a weekend on exactly that. Took the SPARC rotation curve data (~2700 points, 150+ galaxies), built the radial acceleration relation from the raw files and let the engine search. Honest result: it recovered a0 around 1.2e-10 m/s2 like the published fits, and found a different functional form (a log-parabola) that fits exactly as well as the MOND interpolating function, difference of 0.0004 dex. But "exactly as well" is the problem: at the current scatter the data can't tell the forms apart. Which I guess is why the interpolating function debate never dies. No new physics from me, but it was a good way to hit the real wall of the field.
And agreed on residuals, R2 alone is weak. Hold-out validation is already in there, proper residual diagnostics are a fair ask for the roadmap.
Fair point, and nyrikki has it right: the engine never sees units. Everything is normalized to dimensionless magnitudes before the search, so that formula is an empirical fit on pure numbers, not a physical law. I should say that more clearly in the README.
Enforcing dimensional consistency during the search is a known extension (AI Feynman does a version of it) and honestly it's a good roadmap item, a dimensionally sound form would be more trustworthy.
You're right, and I'd go further: the inductive bias isn't incidental, it's the whole product. Short trees over {+, *, sqrt, exp, ...} plus a parsimony penalty is basically Occam's razor made executable. A bias-free learner can't generalize at all (no free lunch), so the honest question is whether this particular bias matches the domain. For physics it has an unreasonably good track record though that's the mystery of physics, not of my library. Two nuances though. The evidence here isn't "a curve fits 8 points" infinitely many do, as you say. It's that a 3-node formula fits them to machine precision (1−R² ≈ 1e-15). Under an MDL view that's not nothing: the probability that such a short description nails 8 independent points exactly, if the truth were some unrelated wiggly curve, is astronomically small. The shortness is the evidence. Second: your adversarial curve would fool Kepler too, and any finite-data method ever. The practical mitigation is the boring one held-out validation, and in the planetary case, extrapolation: the law found on 8 planets keeps working on moons, asteroids and exoplanets. On the tool side I try to keep the failure mode visible rather than hidden: it returns the full accuracy-vs-complexity Pareto front, and the docs say plainly that noise breaks symbolic recovery long before it breaks fit quality. So yes: it finds simple laws when simple laws exist. When they don't, it fails ideally loudly.
Thanks for the formatting tip, noted.
Isn't e different for each planet?
Yes, each planet got its own e (Mercury 0.206, Venus 0.007, Earth 0.017...). The correlation still comes out at 1.00000 because all eccentricities are small while a spans 0.39 to 30 AU — a ≤2% per-planet wobble is invisible to Pearson across two decades of range. That's exactly what makes it a nasty decoy: almost collinear, but not quite.
Mars been the planet with the most eccentric ellipse
Close — Mercury is actually the most eccentric (0.206), Mars is second (0.093). Funny enough, Mars is the famous one precisely because Kepler derived his laws fighting with Tycho's Mars data: its ellipse was just eccentric enough to kill every circular fit. If Tycho had handed him Venus data instead, we might have waited a while longer.
Which heuristic are you using to pick the "best" formula?
Hold-out validation during evolution, then the old CART-style one-standard-error rule: pick the smallest formula whose validation error is within ~1 SE of the best one. Constants get refit with Levenberg-Marquardt before that comparison, so small forms compete at their best. It also returns the full accuracy-vs-size Pareto front so you can override the choice. And to connect it to your noise question: under noise that 1-SE band is exactly where wrong-but-simpler formulas sneak in and tie the true one — the two problems are really the same problem.
Good questions — I ran all three: Semi-minor axis: I added b as a second feature (b = a·sqrt(1−e²), so corr(a,b) = 1.00000 to 5 decimals; Mercury is the only planet where they differ by more than 2%). It still picked a and ignored b completely: T = 164.78·a·sqrt(a), R² = 0.99999998. What saves it on such a nasty collinear decoy is the constant fitting: the law is exact in a and only almost-exact in b, so Levenberg-Marquardt makes that 2% Mercury error decisive. Kepler's 6 planets: works fine, and it's actually nicer — it returned pow(a, 1.500812) explicitly. Six clean points on a power law is plenty. Noise: this is where I have to be honest. With 1% gaussian noise on T the fit is still R² = 0.9996 and a·sqrt(a) is still in there, but wrapped in junk (exp(tanh(...))). At 5% the clean form is gone — it returns a bounded exp(−a²) family that fits well but isn't the law. So fit quality degrades gracefully, symbolic recovery doesn't. Making that part noise-robust is pretty much the open frontier of the whole field, not just of my tool.
What My Project Does
GP_ELITE is a symbolic regression engine in pure Python: given (X, y) data, it searches for a readable mathematical formula linking them, instead of a black-box model.
To show what that means concretely: I gave it nothing but the 8 planets' distance from the Sun and orbital period — 8 data points — and asked for a formula. It returned:
T = a · sqrt(a) (i.e. a^1.5), R² = 1.000000
That's Kepler's Third Law (T² ∝ a³), which took Kepler ~10 years to find in 1618. GP_ELITE found it in ~3 seconds. Reproducible: examples/kepler_demo.py.
v0.2.0 (this week) added the parts that make it reliable: Levenberg-Marquardt constant fitting (constants come back at machine precision — Coulomb's q1·q2/(4πεr²) is recovered exactly), multi-restart with a merged candidate archive, a Pareto front output (the full complexity ↔ accuracy staircase, not just one champion), and a guarded forecasting mode for extrapolating trends beyond your data without the usual GP blow-ups.
Pure Python/NumPy — pip install gp-elite, no compiler, no Julia.
Target Audience
Anyone with small experimental datasets (≤10 variables, 100–5000 points) who wants to understand a relationship, not just predict it: lab engineers, scientists, students. One concrete use case that drove development: battery degradation (SOH) forecasting — the guarded mode gives you an honest bracket of scenarios (a Pareto front from a conservative straight line to richer bounded laws) instead of one overconfident curve. Production-usable for that niche (built-in hold-out validation, regression-tested); not aimed at large-scale ML.
Comparison
vs gplearn (the established pure-Python option): I ran both on the same frozen benchmark — 15 Feynman physics equations, identical data and splits, generous budget for gplearn. Exact symbolic recovery (machine precision): GP_ELITE 10/15 (67%) vs gplearn 6/15 (40%). gplearn recovers the constant-free formulas and stalls as soon as a ½ or a 4π appears (no real constant optimization); LM fitting is what closes that gap. Every number is reproducible: PYTHONHASHSEED=0 python benchmarks/feynman_bench.py 0 15 and benchmarks/duel.py in the repo.
vs PySR / Operon (the state of the art): they are stronger on speed and scale, and I'm not claiming otherwise — but they require a Julia or C++ toolchain. GP_ELITE's whole point is zero barrier: pip install and go.
vs neural nets / gradient boosting: those win on raw accuracy for large data, but give you a black box — GP_ELITE gives you the actual equation.
Honest limits: weak on chaotic targets (tested on Collatz), degrades past ~6 variables with decoy features, and pure Python costs wall-time on big data.
Code (MIT): https://github.com/ariel95500-create/gp-elite
This is an incredibly refreshing piece of work. The decision to use a content-addressed heap over the Leech lattice Lambda_24 canonicalized under the Conway group Co_0 to achieve O(1) structural equality is brilliant, especially for compilers or symbolic reasoning engines.
In my own work with Genetic Programming and Symbolic Regression, memory layout and representation canonicalization are usually the ultimate bottlenecks. When evolving abstract syntax trees (ASTs), we spend an enormous amount of time checking for structural isomorphism or evaluating identical mathematical sub-expressions across islands. Standard hashing works, but pointer/handle comparison via global deduplication at the runtime level feels like a holy grail for symbolic AI.
I am curious about your choice of Lambda_24 over a more traditional high-dimensional cryptographic or geometric hash spatial partitioning. Did you encounter any edge cases where two structurally distinct categorical morphisms or high-density string contents mapped dangerously close to the same lattice point, or does the Conway group canonicalization guarantee enough dispersion to make collisions statistically impossible in practice?
Absolutely love the "Topos" MLIR dialect approach. Keeping an eye on this project!