Formulate
Easy conversions between different styles of expressions. Formulate converts in
either direction between
ROOT (TTreeFormula) and
numexpr style
expressions, and can also render a parsed expression as plain Python using
NumPy functions.
It needs neither ROOT nor numexpr installed — it reads and writes their syntax,
it does not evaluate anything — and it knows the places where the two languages
disagree, such as && and & binding differently against comparisons, or ^
meaning exponentiation in one and XOR in the other.
Full documentation: https://formulate.readthedocs.io/
Installation
Install formulate like any other Python package, ideally inside a virtual environment:
pip install formulate
or using conda:
conda install -c conda-forge formulate
(-c conda-forge is only needed if you don't have the conda-forge channel already configured)
Roadmap and releases
For the roadmap, planned features, breaking changes and versioning please see the roadmap.
Usage
API
The most basic usage involves calling from_$BACKEND and then to_$BACKEND, for example when starting with a ROOT style expression:
>>> import formulate
>>> momentum = formulate.from_root("TMath::Sqrt(X_PX**2 + X_PY**2 + X_PZ**2)")
>>> momentum
Call(function='sqrt', arguments=[BinaryOperator(operator='add', left=BinaryOperator(operator='add', left=BinaryOperator(operator='pow', left=Symbol(name='X_PX'), right=Literal(value=2)), right=BinaryOperator(operator='pow', left=Symbol(name='X_PY'), right=Literal(value=2))), right=BinaryOperator(operator='pow', left=Symbol(name='X_PZ'), right=Literal(value=2)))])
>>> momentum.to_numexpr()
'sqrt((((X_PX ** 2) + (X_PY ** 2)) + (X_PZ ** 2)))'
>>> momentum.to_root()
'TMath::Sqrt((((X_PX ** 2) + (X_PY ** 2)) + (X_PZ ** 2)))'
Similarly, when starting with a numexpr style expression:
>>> my_selection = formulate.from_numexpr("(X_PT > 5) & ((Mu_NHits > 3) | (Mu_PT > 10))")
>>> my_selection.to_root()
'((X_PT > 5) && ((Mu_NHits > 3) || (Mu_PT > 10)))'
>>> my_selection.to_numexpr()
'((X_PT > 5) & ((Mu_NHits > 3) | (Mu_PT > 10)))'
Any parsed expression can also be rendered as Python, using NumPy for the
functions. This direction is output-only — there is no from_python:
>>> momentum.to_python()
'np.sqrt((((X_PX ** 2) + (X_PY ** 2)) + (X_PZ ** 2)))'
Parsing is separate from rendering, so parse once and convert as many times as you like. An expression also reports what it refers to, which is how you work out what a selection needs to read:
>>> expression = formulate.from_root("TMath::Sqrt(px**2 + py**2) > 5 * TMath::Pi() + 1.5")
>>> list(expression.variables)
['px', 'py']
>>> list(expression.named_constants)
['pi']
>>> list(expression.unnamed_constants)
[2, 5, 1.5]
CLI
The package also provides a command-line interface for converting expressions between different styles. To use it, simply run the formulate command followed by the input expression and the desired output.
$ formulate --from-root '(A && B) || TMath::Sqrt(A)' --to-numexpr
((A & B) | sqrt(A))
$ formulate --from-numexpr '(A & B) | sqrt(A)' --to-root
((A && B) || TMath::Sqrt(A))
$ formulate --from-root 'TMath::Sqrt(A)' --to-python
np.sqrt(A)
$ formulate --from-root '(A && B) || TMath::Sqrt(1.23) * e_num**1.2 + 5*pi' --variables
A
B
$ formulate --from-root '(A && B) || TMath::Sqrt(1.23) * e_num**1.2 + 5*pi' --named-constants
exp1
pi
$ formulate --from-root '(A && B) || TMath::Sqrt(1.23) * e_num**1.2 + 5*pi' --unnamed-constants
1.23
1.2
5
Run formulate --help for the full list of options.
Documentation
- Full documentation
- Supported expressions — every operator, function and constant, and how each is spelled in each language
- Common issues — where the languages disagree, and what formulate does about it
- API reference
- Contributing
Release files for formulate 1.1.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| formulate-1.1.0.tar.gz | 87.9 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| formulate-1.1.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 113.7 kB
Release files / formulate-1.1.0.tar.gz
| Download URL | formulate-1.1.0.tar.gz |
|---|---|
| Size | 87.9 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
c08152cbf155e4d4548a062c63d18317f6ea00451159dabb164ca28b82ca47d1
|
|
BLAKE2b-256 checksum How to use checksums |
16d99b4ed6c6d47fbbdba45862c9dee65f160b723e630e73b369b0d0c6b68fb8
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Aug 27, 2026.
Transparency logRelease files / formulate-1.1.0-py3-none-any.whl
| Download URL | formulate-1.1.0-py3-none-any.whl |
|---|---|
| Size | 25.8 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
b4e6fec96cb4847f44d4e02cfc86264a4c3bc127101af59de2010c1b904eb2a6
|
|
BLAKE2b-256 checksum How to use checksums |
d1e7aa3c12d6e50b2732c67f3e7f86b54cf7937310e2e2cf1abbcb3856842d5f
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Aug 27, 2026.
Transparency log