Skip to main content

leaf-complexity

Tests PyPI version Python License: MIT

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 f receives the signed value of each numeric leaf, 1 for 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_complexity is the following. Since in the most general usage case one may sometimes want to choose as wrapping function g a 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 of leaf_complexity itself.

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

Metadata

Release files for leaf-complexity 0.7.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for leaf-complexity 0.7.0
File Size Uploaded
leaf_complexity-0.7.0.tar.gz 15.1 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for leaf-complexity 0.7.0
File Interpreter ABI Platform
leaf_complexity-0.7.0-py3-none-any.whl Python 3 none any Details

Total release size: 24.1 kB

Release files / leaf_complexity-0.7.0.tar.gz

Download URL leaf_complexity-0.7.0.tar.gz
Size 15.1 kB
Tags Source
SHA-256 checksum
How to use checksums
5921c09aad6d242b8126cdc32639246d8688fdb8f0fae5ce9f8526de23b7495c
BLAKE2b-256 checksum
How to use checksums
c02f4a3817ec1ddbaf4d74b14e4592ee29a0c8ae4b1abe9189b49229bf92774f
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.2.0 CPython/3.14.2

Release files / leaf_complexity-0.7.0-py3-none-any.whl

Download URL leaf_complexity-0.7.0-py3-none-any.whl
Size 9.1 kB
Tags Python 3
SHA-256 checksum
How to use checksums
ee623730c543b8f98afdc3414678cb7f2585c3b6c3916123d52f29f52d68d3f5
BLAKE2b-256 checksum
How to use checksums
116e807a94d98c1e81965095b34b5da17f36871a416e648789187e844cd33bc9
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.2.0 CPython/3.14.2

Release history Release notifications | RSS feed

This release

0.7.0 This release

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page