Skip to main content

Compile elementary-function formulas into pure EML (Exp-Minus-Log) form

Project description

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

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 primitivesabs, 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.1}
}

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

emltree-0.1.1.tar.gz (39.6 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

emltree-0.1.1-py3-none-any.whl (12.9 kB view details)

Uploaded Python 3

File details

Details for the file emltree-0.1.1.tar.gz.

File metadata

  • Download URL: emltree-0.1.1.tar.gz
  • Upload date:
  • Size: 39.6 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.8.17

File hashes

Hashes for emltree-0.1.1.tar.gz
Algorithm Hash digest
SHA256 cc9cd8286944adedb8cf94bc704d6799cfce833ab86f6245450ee554c1773648
MD5 d4e40fba7a3ba8d75b60be8b861dd9d7
BLAKE2b-256 5db88fa411fef3b38aa1160a081278c99e70a8a0f4a23147b8b6c3e29c0644b3

See more details on using hashes here.

File details

Details for the file emltree-0.1.1-py3-none-any.whl.

File metadata

  • Download URL: emltree-0.1.1-py3-none-any.whl
  • Upload date:
  • Size: 12.9 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.8.17

File hashes

Hashes for emltree-0.1.1-py3-none-any.whl
Algorithm Hash digest
SHA256 13fe8dd0dd37c18707ceb6fcc33cc5fa7d300ac62ea310edc95e05db90be7840
MD5 1b306b591a7fda421ed7762459d757ec
BLAKE2b-256 47ebb0c29bb1f3d2535e3e940436985cb6378a7c735356b07c4dd1d93bb661ca

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page