emlmath is a small Rust crate that explores the idea from All elementary functions from a single binary operator by Andrzej Odrzywołek. The project implements the paper's eml(x, y) = exp(x) - log(y) construction and builds a library of elementary functions on top of that single binary operator.
The repository currently contains:
- A
Complexnumber type with the operations needed to evaluate elementary functions. - A
ComplexBalltype for midpoint-plus-radius error bounds during tree evaluation. - A low-level
EmlExprtree that only uses1, variables, and theeml(left, right)operator. - A higher-level
ScientificExprtree with familiar functions likesin,sqrt,atanh, andpow, plus a compiler toEmlExpr. - A small demo binary in
src/main.rs. - A copy of the reference paper in
doc/2603.21852v2.pdf.
Most math libraries start with many primitive operations and functions. This project goes in the opposite direction: it treats eml as the only primitive operator and derives everything else from it. That makes the crate useful as:
- A reference implementation of the paper's formulas.
- A playground for inspecting how large elementary expressions become when reduced to a single operator.
- A testbed for branch-cut and complex-analysis behavior when translating standard formulas into
emlform.
The crate exposes two main expression types:
ScientificExpr: ergonomic constructors for standard math expressions.EmlExpr: the compiled single-operator form.
Typical workflow:
- Build a
ScientificExprorEmlExpr. - Optionally convert a
ScientificExprintoEmlExprwithto_eml(). - Evaluate the expression with real variable assignments using
eval_real/eval_real_scientific, or evaluate directly with complex assignments viaeval.
For bound tracking, the crate also exposes ball-arithmetic evaluators:
eval_ball/eval_ball_scientificfor explicitComplexBallassignmentseval_real_ball/eval_real_ball_scientificfor real inputs with scalar radii
Supported derived operations include:
- Arithmetic:
add,sub,mul,div,pow,reciprocal - Constants:
0,1,2,1/2,e,i,pi - Elementary functions:
exp,ln,sqrt - Trigonometric and hyperbolic functions:
sin,cos,tan,sinh,cosh,tanh - Inverse functions:
asin,acos,atan,asinh,acosh,atanh
emlmath can evaluate expression trees with conservative midpoint-plus-radius bounds using ComplexBall. This is useful when you want an enclosure for the result instead of a single point value.
Example:
use emlmath::{ComplexBall, ScientificExpr, eval_ball_scientific, sx};
fn main() {
let expr = ScientificExpr::ln(ScientificExpr::add(sx(), ScientificExpr::one()));
let value = eval_ball_scientific(
&expr,
&[("x", ComplexBall::from_real(0.5, 1e-6))],
)
.unwrap();
println!("value = {}", value);
}The ball evaluator is conservative and explicit about unsafe regions:
lnand theemloperator reject balls that contain zero.lnandeml_logreject balls that cross the nonpositive real axis.- Division rejects denominator balls that contain zero.
When those cases occur, evaluation returns a BallEvalError instead of silently producing an invalid bound.
EML compilation is branch-sensitive because eml(x, y) is defined in terms of exp and ln, and the compiled ln tree crosses the negative real axis internally.
This crate implements the paper's partial branch-handling solution in two places:
EmlExprevaluation uses a mirrored logarithm branch internally so that compiledln(z)matches the standard principal branch across the negative real axis.- The compiler uses a sign-corrected internal
iwhen building derivedEmlExprformulas that depend on the branch convention.
These choices are enough to make compiled ln and the mirrored-log branch behavior consistent with the intended principal-branch output.
use emlmath::{ScientificExpr, eval_real_scientific, sx, sy};
fn main() {
let expr = ScientificExpr::add(ScientificExpr::sin(sx()), ScientificExpr::sqrt(sy()));
let eml = expr.to_eml();
let value = eval_real_scientific(&expr, &[("x", 0.5), ("y", 9.0)]).unwrap();
println!("scientific = {:?}", expr);
println!("eml nodes = {}", eml.node_count());
println!("value = {}", value);
}The bundled demo binary uses the same idea with EmlExpr directly:
The bundled demo binary builds the expression as ScientificExpr, compiles it to EmlExpr for inspection, and prints both the direct point evaluation and a ball-arithmetic enclosure:
cargo runAt the time of writing, that example produces an EmlExpr with 715 nodes for sin(x) + sqrt(y), together with the direct value and a conservative error ball. This shows both how quickly the single-operator representation expands and how the new bound-tracking path can be used alongside the direct evaluator.
Build and test with standard Cargo commands:
cargo run
cargo testThe crate currently has no external dependencies.
This is an experimental math/code exploration project, not a production numerical library. A few practical constraints are worth knowing:
- The public API is still minimal and may change freely.
- Numerical behavior follows the crate's custom
Compleximplementation rather than a battle-tested external library. - Branch handling is intentionally tailored to make the compiled
emlforms agree with the project's expected logarithm behavior. - The paper's branch workaround is only partial: compiled
lnis validated, but some higher derivedEmlExprconstructors still do not match directScientificExprevaluation when complex intermediate values appear. - In particular, compiled trigonometric/hyperbolic and some inverse-function formulas still have known equivalence gaps; the test suite keeps those checks as ignored regression targets until the compiler identities are re-derived correctly.
- The generated
EmlExprtrees can become very large very quickly. - Ball-arithmetic bounds are conservative and can widen substantially on deep compiled
EmlExprtrees. - Some expressions that are well-defined at a point may still be rejected in ball mode if the enclosing ball touches a singularity or branch cut.
This project is available under the terms in LICENSE.