Compute a sum or other complexity measure over all atoms of a symbolic expression
Project description
leaf-complexity
Compute a sum or other complexity measure over all atoms of a symbolic expression.
Overview
Any SymPy expression is internally a tree of heads (operators, function symbols, containers) and leaves (numbers, symbols), which can be inspected with sympy.srepr or printed with sympy.printing.tree. The total number of indivisible subexpressions of that tree is the simplest measure of complexity — but expressions with the same number of leaves can be very far apart in simplicity: x + 2 and x + 1000000 count the same.
leaf_complexity directly extends the leaf count by weighing each leaf by its absolute numeric value and then taking the sum. If a leaf is non-numeric, it is counted by default as 1. Even if already atomic, rational numbers are decomposed into numerator and denominator, complex numeric literals into real and imaginary part, and dictionaries are scanned over their values.
This is a faithful port of the Wolfram Language resource function LeafComplexity, by the same author.
Installation
pip install leaf-complexity
Usage
from sympy import symbols, log
from leaf_complexity import leaf_complexity
x, y = symbols('x y')
leaf_complexity(expr) gives the sum of all the numeric indivisible subexpressions in expr — so expressions with the same leaf count may have different complexity:
leaf_complexity(x + 2) # 5
leaf_complexity(x + 10) # 13
leaf_complexity(expr, f) applies a function f to each indivisible subexpression in expr and takes the total:
leaf_complexity(x + 1000000, lambda v: log(v, 10)) # 7
leaf_complexity(expr, f, g) applies f to each indivisible subexpression and recursively applies to it another wrapping function g, as s = g(s, f(leaf)):
from sympy import exp, Mul
rng = [1, 2, 3, 4, 5]
leaf_complexity(rng, exp, Mul) # exp(16)
leaf_complexity(rng, exp, lambda s, v: exp(v)) # exp(exp(5))
leaf_complexity(rng, exp, lambda s, v: exp(s)) # exp(exp(exp(exp(exp(E)))))
Setting g to an additive function recovers the other usage cases up to two arguments.
Parameters
| Parameter | Default | Description |
|---|---|---|
expr |
(required) | SymPy expression, number, or (nested) container |
f |
None |
function applied to each leaf before totaling (None means absolute value) |
g |
None |
binary wrapping function recursively applied as g(s, f(leaf)) in place of the sum; requires f |
heads |
True |
consider also the subexpression heads as leaves |
Through the option heads set to False, only proper leaf nodes are counted (parent nodes count 0):
leaf_complexity(x + 1, heads=False) # 3
leaf_complexity(x + 1) # 4
Details
-
It works on any expression: SymPy expressions, plain numbers,
fractions.Fraction,complex, and arbitrarily nested lists, tuples and dictionaries (the analog of WL Associations — scanned over their values only). -
A custom
freceives the signed value of each numeric leaf,1for heads and non-numeric leaves, and the infinity itself for infinite leaves. The Wolfram "unary" wrapping usage corresponds in Python to a two-argument callable ignoring one of its arguments. -
An important detail in the definition and design of
leaf_complexityis the following. Since in the most general usage case one may sometimes want to choose as wrapping functionga product instead of the default sum, then in order to avoid returning identically null results, all usage cases are implemented as recursions starting from initial condition 1 instead of 0. This in practice induces the somewhat awkward "correspondence principle" that if all leaves are equal to 1 or −1,leaf_complexity(expr)equals the leaf count (heads included) plus 1. One may desire to see complete equality with the leaf count in this special case, but a better trade-off seems to maintain the internal consistency and generality ofleaf_complexityitself.
Applications
leaf_complexity can work as a measure of complexity for algebraic expressions — for instance to steer sympy.simplify away from large numbers, the analog of ComplexityFunction in the Wolfram Language:
from sympy import simplify
a, b = symbols('a b')
simplify(2*log(a) + 4*log(-4)) # 2*log(a) + log(256) + 4*I*pi
simplify(2*log(a) + 4*log(-4), measure=leaf_complexity) # 2*log(a) + 8*log(2) + 4*I*pi
Functions like algebraic-range tend to overproduce complex closed forms; leaf_complexity allows to select only the simpler ones:
simple = [e for e in closed_forms if leaf_complexity(e) <= 30]
The resulting numeric distribution can still be nearly uniform.
Performance
leaf_complexity produces exactly the same values as the Wolfram Language LeafComplexity resource function on identical expression trees (verified case-for-case). Because the work is a pure recursive tree scan — with no parsing hot-path to exploit — the compiled Wolfram kernel is about 2–3× faster, e.g. on large expanded polynomials; the trade-off is the lightweight SymPy-only portability. See benchmark/BENCHMARK.md for the full table and methodology.
See also
This Python package has been inspired by the following Wolfram functions:
- LeafComplexity (resource function contributed by the same author);
- LeafCount (built-in Wolfram Language function).
License
MIT
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 leaf_complexity-0.7.0.tar.gz.
File metadata
- Download URL: leaf_complexity-0.7.0.tar.gz
- Upload date:
- Size: 15.1 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.14.2
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
5921c09aad6d242b8126cdc32639246d8688fdb8f0fae5ce9f8526de23b7495c
|
|
| MD5 |
23b9b7172eaa4b92312aca021ed5a703
|
|
| BLAKE2b-256 |
c02f4a3817ec1ddbaf4d74b14e4592ee29a0c8ae4b1abe9189b49229bf92774f
|
File details
Details for the file leaf_complexity-0.7.0-py3-none-any.whl.
File metadata
- Download URL: leaf_complexity-0.7.0-py3-none-any.whl
- Upload date:
- Size: 9.1 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.14.2
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
ee623730c543b8f98afdc3414678cb7f2585c3b6c3916123d52f29f52d68d3f5
|
|
| MD5 |
3590b4a87e7c1c4bfa63943c5cc6ee22
|
|
| BLAKE2b-256 |
116e807a94d98c1e81965095b34b5da17f36871a416e648789187e844cd33bc9
|