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),logof 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 appliesexp()to its second operand, so adding values past ~709 overflows float64. Integer/decimal constants avoid this internally (binary decomposition with a multiplicative odd step), butx + ywith hugeyis 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 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.1}
}
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
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
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
cc9cd8286944adedb8cf94bc704d6799cfce833ab86f6245450ee554c1773648
|
|
| MD5 |
d4e40fba7a3ba8d75b60be8b861dd9d7
|
|
| BLAKE2b-256 |
5db88fa411fef3b38aa1160a081278c99e70a8a0f4a23147b8b6c3e29c0644b3
|
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
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
13fe8dd0dd37c18707ceb6fcc33cc5fa7d300ac62ea310edc95e05db90be7840
|
|
| MD5 |
1b306b591a7fda421ed7762459d757ec
|
|
| BLAKE2b-256 |
47ebb0c29bb1f3d2535e3e940436985cb6378a7c735356b07c4dd1d93bb661ca
|