Skip to main content

emltree

Compile elementary-function formulas into pure EML (Exp-Minus-Log) form, where the only operator is

eml(x, y) = exp(x) − ln(y)

and the only constant is 1.

Paper

This project is an implementation of ideas introduced in:

Andrzej Odrzywolek. All elementary functions from a single operator. arXiv:2603.21852 (2026). https://arxiv.org/abs/2603.21852

The paper proves that the single binary operator eml(x, y) = exp(x) − ln(y) together with the constant 1 generates the entire scientific-calculator basis (arithmetic, roots, logs, all trig and hyperbolic functions, the constants e, π, i). Every elementary expression becomes a binary tree whose only internal node is eml. This package is the inverse direction: it takes an ordinary formula and compiles it into that tree. A sibling Rust crate, OxiEML, covers the Rust ecosystem; a zero-dependency JS/TypeScript port lives in js/ (npm: emltree); this package is the Python side.

Quick start

uv venv --python 3.11
uv pip install -e ".[dev]"

# Compile a formula
uv run emltree "sin(x) + exp(y)" -v x -v y

# ASCII tree
uv run emltree "log(x)" -v x -f tree

# Numerical sanity check
uv run emltree "sin(x)**2 + cos(x)**2" -v x=0.9 --verify

Library use

from emltree import compile_formula, evaluate

tree = compile_formula("sqrt(x**2 + y**2)", variables=["x", "y"])
print(tree.to_nested())
print(evaluate(tree, {"x": 3.0, "y": 4.0}))   # ≈ 5+0j

The returned tree is an immutable ADT (One / Var / Eml) — walk it, hash it, render as RPN (exp(x) is x 1 E), or ship it into an FPGA/analog circuit as the paper suggests.

Evaluation is vectorised: pass numpy arrays as bindings and the tree evaluates elementwise.

import numpy as np
xs = np.linspace(-2, 2, 1000)
evaluate(tree, {"x": xs, "y": xs})   # array of 1000 values

Why the trees are large

This compiler is compositional, not optimal. Every primitive bottoms out in exp / ln / sub, so sin(x) produces a tree with hundreds of nodes. The paper's direct-search results (Table 4) are vastly shorter — integrating that search is one of the open directions below.

Numerical caveats

  • Branch cuts: outside their real domains (asin(2), acosh(-2), log of negatives, …) results flow through complex branch cuts and may land on a non-principal branch — or, where float fuzz compounds, off-sheet entirely (paper §4.1). On the usual real domains everything matches sympy to ~1e-7.
  • Addition overflow: add_'s expansion applies exp() to its second operand, so adding values past ~709 overflows float64. Integer/decimal constants avoid this internally (binary decomposition with a multiplicative odd step), but x + y with huge y is an inherent ceiling of the encoding.

Tests

uv run pytest          # Python
cd js && node --test   # JS port

vectors.json holds cross-language cases — formula, bindings, and an expected value taken from Python's math module, so it is independent of both implementations. tests/test_vectors.py and js/vectors.test.js each run every case. A change that makes one port disagree with the other fails there, rather than waiting to be spotted during the next hand-port (which is how the 0.1.1 atan sign bug surfaced). New primitives should land with a vector.

Contributing

Contributions are very welcome — this is an early-stage package and there's plenty of room to improve. A few directions that would make a great first PR:

  • Shorter trees — hand-curated or searched EML identities to replace the naive compositional expansions (see paper Table 4).
  • More primitives — abs, sign, floor, ceil, erf, etc. (many require tricks; see the paper's supplementary).
  • Better output — LaTeX, GraphViz / Mermaid, Jupyter rich repr.
  • Torch evaluation — numpy bindings already vectorise; a torch evaluator would make EML trees differentiable end-to-end.
  • Optional Rust backend — PyO3 bindings to OxiEML for expensive search and symbolic regression.
  • Docs & examples — walk-throughs of the paper's identities, notebooks showing symbolic-regression use cases.

Please open an issue to discuss before sending a large PR. Bug reports, documentation fixes, and additional test cases are also very welcome — no contribution is too small.

Development

uv venv --python 3.11
uv pip install -e ".[dev]"
uv run pytest

License

MIT — see LICENSE.

Citation

If you use emltree in academic work, please cite both the paper and the software:

@article{odrzywolek2026eml,
  title   = {All elementary functions from a single operator},
  author  = {Odrzywolek, Andrzej},
  journal = {arXiv preprint arXiv:2603.21852},
  year    = {2026}
}

@software{hsiao2026emltree,
  author  = {Hsiao, Wei-Chien},
  title   = {emltree: a Python compiler from elementary formulas to EML trees},
  year    = {2026},
  url     = {https://github.com/gba3124/emltree},
  version = {0.1.2}
}

Metadata

Release files for emltree 0.1.3

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for emltree 0.1.3
File Size Uploaded
emltree-0.1.3.tar.gz 41.4 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for emltree 0.1.3
File Interpreter ABI Platform
emltree-0.1.3-py3-none-any.whl Python 3 none any Details

Total release size: 54.8 kB

Release files / emltree-0.1.3.tar.gz

Download URL emltree-0.1.3.tar.gz
Size 41.4 kB
Tags Source
SHA-256 checksum
How to use checksums
5fd4c8588467f6f31c4261228acb8af81fc7125d0497ba38ce990fb4c0374812
BLAKE2b-256 checksum
How to use checksums
2520ffabb83ace6035c523ca1b0232116e400f828bb274667305de91a7903f16
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via uv/0.8.17

Release files / emltree-0.1.3-py3-none-any.whl

Download URL emltree-0.1.3-py3-none-any.whl
Size 13.3 kB
Tags Python 3
SHA-256 checksum
How to use checksums
f314f78191da6f0ef6d4afab182fcdb91b2548a98a9af7822827fa6c6388ec92
BLAKE2b-256 checksum
How to use checksums
ba18b057552dd21b499c0dfb62bb2246e60d16d0ebb381bc4a58932bd3cb3c18
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via uv/0.8.17

Release history Release notifications | RSS feed

This release

0.1.3 This release

2 release files

0.1.1

2 release files

0.1.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page