All elementary functions from a single operator: eml(x, y) = exp(x) − ln(y)
Project description
exp-minus-log — all elementary functions from a single operator
pip install exp-minus-log
from eml import eml, parse_rpn, evalf
A Python implementation of the EML (Exp-Minus-Log) operator from
A. Odrzywołek, All elementary functions from a single operator, arXiv:2603.21852, April 2026.
The paper shows that the binary operator
eml(x, y) = exp(x) - ln(y)
paired with the constant 1, suffices to construct every primitive of
a scientific calculator: integers and fractions, the constants e,
π, i, the four arithmetic operations, the trig / inverse-trig /
hyperbolic / inverse-hyperbolic family, exponentiation and logarithms
to arbitrary base, √, sigmoid, and so on.
This package gives that operator the full Python toolchain: an expression-tree compiler, a single-instruction stack machine, a sympy bridge, gradient-based symbolic regression, exhaustive search for shortest chains, mpmath verification at arbitrary precision, and a matplotlib renderer that draws each chain as the EML circuit symbol from the paper.
Quickstart
from eml import eml, parse_rpn
# Two canonical identities:
eml(1, 1) # 2.718281828459045 (the constant e)
parse_rpn("11xE1EE")(x=7.5)
# 2.0149030205422647 (== math.log(7.5); the ln chain from Eq. (5))
Every Table-1 primitive is one call away:
from eml.library import build_unary, build_constant
build_unary("sin", "x")(x=1.0) # sin(1) via EML
build_constant("pi")() # π via EML (K=233)
Verify the whole library at 40-digit mpmath precision:
python examples/07_verify_library.py
# ...
# 41/41 passed
Modules
| module | what it does |
|---|---|
eml.core |
scalar / NumPy ufunc / mpmath implementations of eml, edl, −eml |
eml.tree |
AST for the grammar S → 1 | eml(S, S), RPN round-trip, sympy bridge |
eml.library |
every primitive of Table 1 expressed as a concrete EML chain (verified) |
eml.sympy_eml |
a SymPy Function EmlFn, with auto-eval, fdiff, evalf, rewrite |
eml.vm |
single-instruction stack VM (compile / disassemble / trace / bytestream) |
eml.search |
brute-force shortest-EML search (the Direct search column of Table 4) |
eml.regression |
the level-n master formula (5·2ⁿ − 6 simplex parameters), Adam, snap-to-vertex |
eml.verify |
Schanuel-witness verification (γ, A) at arbitrary mpmath precision |
eml.viz |
ASCII tree + matplotlib EML-circuit renderer (Fig. 2) |
eml.cousins |
EDL (exp/ln, paired with e) and −eml(y,x) (ln−exp, paired with −∞) |
Examples
PYTHONPATH=. python examples/01_canonical_identities.py # ln, exp, e, 0
PYTHONPATH=. python examples/02_compile_function.py # build any primitive
PYTHONPATH=. python examples/03_visualise.py # render Fig.-2-style trees
PYTHONPATH=. python examples/04_vm_bytecode.py # run on the EML stack VM
PYTHONPATH=. python examples/05_search.py # find shortest chains
PYTHONPATH=. python examples/06_symbolic_regression.py # recover exp(x) from data
PYTHONPATH=. python examples/07_verify_library.py # verify all 41 primitives
PYTHONPATH=. python examples/08_sympy_calculus.py # symbolic diff/simplify
PYTHONPATH=. python examples/09_cousins.py # EDL and −EML in action
Tests
PYTHONPATH=. python -m pytest tests/
# 77 passed
Notes on numerical edge cases
EML chains intentionally route through ln(0) = −∞ and exp(−∞) = 0
when constructing the constant 0, the negation function −x, and a
few of their dependents (cf. Table 4 column "without extended reals").
NumPy honours this via signed zeros and infinities, so the chains
"just work" in IEEE-754; the core.eml implementation suppresses the
benign divide-by-zero RuntimeWarning that the principal log(0) raises.
The principal-branch convention means the chain that the paper writes
for i lands on −i — see §4.1 ("manually correct i sign"). The
helper eml.core.fix_i_sign performs that one-line correction.
Why complex numbers?
The paper requires it (page 5):
"Computations must be done in the complex domain, e.g. generating constants like
iandπrequires evaluatingln(−1), soeml(x, y)internally operates over ℂ using the principal branch."
A real-only EML cannot:
- generate
i,π,−1,sin x,cos x,tan x, ... — every chain for these routes throughln(z)for somez < 0; - even compute
ln 0 = −∞portably (some platforms trap it).
So eml.core.eml always promotes to complex128 internally. When the
inputs are real and the output is real (within numerical noise), the
return value is silently demoted back to float64 so the API still
feels "real" to the user. If you want explicit numpy floats end-to-end,
restrict yourself to chains that don't pass through any negative-real
log argument (essentially: +, *, exp, ln of positives) and
unwrap with .real after each call.
Precision: bit-exact agreement with math.*
The natural floating-point evaluation of an EML chain accumulates
~0.5 ULP per node, so e.g. build_constant("pi")() lands ~2 ULPs
away from math.pi. Most users don't need bit-exact, but if you do:
from eml.core import evalf
from eml.library import build_constant, build_unary
import math
evalf(build_constant("pi"), real=True) == math.pi # True
evalf(build_unary("sin", "x"), {"x": 1.0}, real=True) == math.sin(1.0) # True
evalf(build_unary("ln", "x"), {"x": 2.0}, real=True) == math.log(2.0) # True
evalf evaluates the chain at 30-digit mpmath precision and rounds
once to complex128. Any chain that fits in 30 digits is then
correctly rounded — no ULP slippage from intermediate evaluations.
"Should I just hard-code math.pi as a leaf?"
No. That would defeat the entire point of a single-operator system.
The interesting fact about EML is that π = ln(−1)/i as an EML
expression — exposing it as a leaf would erase that. Use evalf if
you need bit-exact agreement; use the natural numpy path if you don't
care about the last bit.
Use cases
See USECASES.md for fifteen concrete and speculative
applications of the EML operator, grouped by maturity and pointing at
the relevant module / example for each.
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 exp_minus_log-0.1.1.tar.gz.
File metadata
- Download URL: exp_minus_log-0.1.1.tar.gz
- Upload date:
- Size: 42.7 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
cf9d7ccd4914a1dfe03e3a60d98074cff728e6036a3a6dc1b2858dcf71adc74a
|
|
| MD5 |
7ee73ab1515fc1cc6382154a56ebae30
|
|
| BLAKE2b-256 |
028b8f562b15c52d8dbbf23e5e38a7df1079ca7059227f92f7adbd308ca8261f
|
Provenance
The following attestation bundles were made for exp_minus_log-0.1.1.tar.gz:
Publisher:
publish.yml on Inknyto/eml
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
exp_minus_log-0.1.1.tar.gz -
Subject digest:
cf9d7ccd4914a1dfe03e3a60d98074cff728e6036a3a6dc1b2858dcf71adc74a - Sigstore transparency entry: 1496739732
- Sigstore integration time:
-
Permalink:
Inknyto/eml@ae1f62a76bd0eb8cae92e0d8de5b7ca51c036b56 -
Branch / Tag:
refs/tags/v0.1.1 - Owner: https://github.com/Inknyto
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@ae1f62a76bd0eb8cae92e0d8de5b7ca51c036b56 -
Trigger Event:
push
-
Statement type:
File details
Details for the file exp_minus_log-0.1.1-py3-none-any.whl.
File metadata
- Download URL: exp_minus_log-0.1.1-py3-none-any.whl
- Upload date:
- Size: 35.0 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
58d5ba37a2663c4ebf9438f424e0a8b9d19eb6c3b8475faded1a54ba1545e930
|
|
| MD5 |
2cdd2c72f642d6737240f40b8c8413c8
|
|
| BLAKE2b-256 |
355b5abbcf1b27b33bb19b92174e26a492de7b40b798503a65eb4ad8d9649235
|
Provenance
The following attestation bundles were made for exp_minus_log-0.1.1-py3-none-any.whl:
Publisher:
publish.yml on Inknyto/eml
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
exp_minus_log-0.1.1-py3-none-any.whl -
Subject digest:
58d5ba37a2663c4ebf9438f424e0a8b9d19eb6c3b8475faded1a54ba1545e930 - Sigstore transparency entry: 1496739893
- Sigstore integration time:
-
Permalink:
Inknyto/eml@ae1f62a76bd0eb8cae92e0d8de5b7ca51c036b56 -
Branch / Tag:
refs/tags/v0.1.1 - Owner: https://github.com/Inknyto
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@ae1f62a76bd0eb8cae92e0d8de5b7ca51c036b56 -
Trigger Event:
push
-
Statement type: