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/
Install formulate like any other Python package, ideally inside a virtual environment:
pip install formulateor 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)
For the roadmap, planned features, breaking changes and versioning please see the roadmap.
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]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
5Run formulate --help for the full list of options.
- 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