Skip to content
← Writing

Why I rewrote RIES in Rust

Bringing Robert Munafo’s solver to the command line, Python, and the browser.

By Maxwell SantoroEssayUpdated About 3 min read

Give RIES 2.506628274631 and it can find √(2π) among the candidate expressions. What drew me to Robert Munafo’s solver was the reversal. Usually I start with a formula and see what number comes out. Here I could start with a number and ask where it might have come from.

The first match for 2.506628274631

x=2π

Rank
1
Complexity score
55
Reported absolute error
≈ 4.44 × 10−16

Output from the bundled ries-rs 2.1.0 WebAssembly solver at level 5, using complexity ranking and up to five matches. Try this search.

I rewrote RIES in Rust because I wanted to ask that question from the command line, Python, and the browser. I also wanted runs I could repeat and output other programs could read. The original C program was easy to try in a terminal. Carrying the results into another tool took more work.

What stayed the same

The search model is unchanged. The solver enumerates valid postfix expressions up to a complexity limit. Some of those expressions contain x; those become candidate left-hand sides. Others are constant-only expressions; those become candidate right-hand sides. The search then tries to solve equations of the form LHS(x) = RHS numerically and ranks the matches it finds.

The search is bounded by the chosen complexity limit and available symbols. A numerical match gives you a candidate relation to investigate; it does not prove an identity.

What Rust changed

The rewrite separates expression generation, matching, and ranking into modules, which made two other things practical: Python bindings and a WebAssembly build from the same engine. Native builds can spread generation across cores.

For scripting, searches print structured JSON and record run manifests, so a script can inspect results without scraping terminal formatting. Deterministic mode fixes the result ordering, so two runs can be compared directly.

Parallel generation only helped searches dominated by generating expressions. For searches dominated by matching and refinement it did almost nothing — which is what sent me to the profiler. The performance follow-up describes the ranking bug that turned up there, and the separate turbo mode for parallel matching.

Why the browser build mattered

The browser version runs the solver locally, compiled to WebAssembly. A reader can enter a number and try a search immediately, without installing the command-line tool.

That made it practical to put a number form on the project page and open the solver with the target already filled in. Shared URLs can carry the search settings too.

The browser build also surfaced every packaging detail: asset paths have to work below the site’s project URL, the interface has to wait for WebAssembly initialization, and its runtime dependencies have to be vendored. The browser uses the sequential solver; native turbo mode is a different execution path.

You can explore the project on the project page, try the live demo, or browse the source on GitHub.

Back to Writing

Topics: rust, wasm, math, ries

The project behind it

View all →