Generic bidirectional rake-compress tree contraction for JAX
Project description
JAX Bidirectional Tree Rake–Compress
Generic, topology-independent rake–compress tree contraction and reverse recovery for JAX.
The package separates a one-time CPU planning step from repeated numerical execution:
parents ──CPU──> TreeContractionPlan
│
node/path arrays ──JAX───┼──> root summary + residual tape
│
root output ─────────────┴──> output at every original node
The numerical meaning of a contraction is supplied by a small user-defined algebra. The same topology plan can therefore evaluate subtree reductions, conditional value functions, probabilistic messages, affine maps, and other fixed-shape tree computations.
Why bidirectional?
An ordinary tree reduction returns only a root value. Many applications also need quantities at the nodes eliminated during the reduction. For example, parallel optimal-control factorization must recover every intermediate value function, and its solve must propagate a root solution back to all states.
tree_contract therefore returns both a root summary and a residual tape.
tree_expand visits the same plan backward and uses application-defined
recovery operations to reconstruct one output per original node.
Installation
Install from PyPI:
python -m pip install jax-bidirectional-tree-rake-compress
From a source checkout:
python -m pip install -e .
For development:
python -m pip install -e '.[dev]'
pytest
The package uses whichever JAX accelerator installation is available. See the JAX installation guide for platform-specific GPU or TPU wheels.
Quick start: subtree sums
Each node starts with a local scalar. An edge path starts at zero. Raking and compressing accumulate completed subtrees, while expansion reconstructs the subtree sum at every node.
import jax
import jax.numpy as jnp
import jax_bidirectional_tree_rake_compress as jtrc
class SubtreeSum:
def rake(self, path, leaf):
return path + leaf, leaf
def combine_branches(self, left, right):
return left + right
def absorb_branch(self, node, message):
return node + message
def compress(self, left, middle, right):
return left + middle + right, (middle, right)
def expand_compress(self, residual, parent_output, child_output):
del parent_output
middle, right = residual
return middle + right + child_output
def expand_rake(self, residual, parent_output):
del parent_output
return residual
parents = [-1, 0, 0, 1, 1, 3, 2]
plan = jtrc.make_tree_contraction_plan(parents) # CPU, once
node_values = jnp.arange(1, 8, dtype=jnp.float32)
edge_values = jnp.zeros(plan.num_edges, dtype=jnp.float32)
algebra = SubtreeSum()
@jax.jit
def run(nodes, edges):
root, tape = jtrc.tree_contract(plan, nodes, edges, algebra)
return root, jtrc.tree_expand(plan, tape, root, algebra)
root, subtree_sums = run(node_values, edge_values)
The result is
root = 28
subtree_sums = [28, 17, 10, 10, 5, 6, 7]
All numerical payloads may be PyTrees of arrays. Every leaf needs a leading node or edge axis, and every value in a role must have the same static shape.
Public API
make_tree_contraction_plan(parents, root=None)performs validated CPU preprocessing and returns a JAX PyTree of integer schedule arrays.tree_contract(plan, nodes, paths, algebra)returns the root summary and a numerical recovery tape.tree_reduce(...)returns only the root summary.tree_expand(plan, tape, root_output, algebra)reverses the schedule.tree_contract_and_expand(...)composes contraction, root work, and expansion.plan_statistics(plan)reports topology counts without copying schedule values back from a device.
The plan orders input path summaries by plan.edge_children: one incoming
edge for each non-root node. plan.edge_parents gives the corresponding
parent indices.
JAX transformations
The executor is written in pure JAX. It can be used inside jax.jit, mapped
over batches with jax.vmap, and differentiated with jax.grad, jax.jvp,
or jax.vjp. The topology indices are discrete and are not differentiated.
Plans can either be closed over by a compiled function or passed as ordinary PyTree arguments. Closing over a plan specializes compilation to that topology; passing it as an argument permits reuse by plans with the same round and array shapes.
Algorithmic notes
Every non-root node is removed exactly once by either:
- rake: close an active leaf component and send a branch message to its parent;
- compress: eliminate a unary middle node and join its two oriented path summaries.
Sibling rakes never race. Messages targeting the same parent are combined by a CPU-precomputed balanced reduction before the parent is updated. This makes the generic implementation independent of atomic addition and supports any compatible associative branch operation.
For bounded-degree trees, the plan has linear work and logarithmic contraction depth. High-degree branch reductions are balanced as well; actual device kernel count and fusion depend on JAX and XLA.
See the design document for the precise invariants and the optimal-control mapping for the intended LQR application.
Current limitations
- Numerical roles have uniform static shapes. Variable-dimensional tree problems need padding, dimension bucketing, or separate compiled plans.
- The planner is deterministic but does not optimize a hardware cost model.
- A pure-JAX call can lower to multiple device kernels; this API does not promise one dispatch.
- The current planner is for static rooted trees. Dynamic topology updates are out of scope.
Development
ruff check .
ruff format --check .
pytest
python -m build
python -m twine check dist/*
Benchmarks are described in benchmarks/README.md.
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 jax_bidirectional_tree_rake_compress-0.1.0.tar.gz.
File metadata
- Download URL: jax_bidirectional_tree_rake_compress-0.1.0.tar.gz
- Upload date:
- Size: 22.9 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
61c6a50e89f1acc9863755101e3819ba503942a56928143c7b536b6c2f0198b1
|
|
| MD5 |
a0c6d2fbdc1c8eb0f88f4ef0a0646cfc
|
|
| BLAKE2b-256 |
32fcbc3b301cd08dead3f4fd63acecc37b58c5af5a5877af7f32e7cf1950e264
|
Provenance
The following attestation bundles were made for jax_bidirectional_tree_rake_compress-0.1.0.tar.gz:
Publisher:
package.yml on joaospinto/jax-bidirectional-tree-rake-compress
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
jax_bidirectional_tree_rake_compress-0.1.0.tar.gz -
Subject digest:
61c6a50e89f1acc9863755101e3819ba503942a56928143c7b536b6c2f0198b1 - Sigstore transparency entry: 2211057385
- Sigstore integration time:
-
Permalink:
joaospinto/jax-bidirectional-tree-rake-compress@d62fcf0e43138376d2fff07210b561ec23056ce7 -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/joaospinto
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
package.yml@d62fcf0e43138376d2fff07210b561ec23056ce7 -
Trigger Event:
release
-
Statement type:
File details
Details for the file jax_bidirectional_tree_rake_compress-0.1.0-py3-none-any.whl.
File metadata
- Download URL: jax_bidirectional_tree_rake_compress-0.1.0-py3-none-any.whl
- Upload date:
- Size: 12.2 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
71a5683962bd581be6bb35d8b88665f3f716a92e35fea55ba05af5c800f632fb
|
|
| MD5 |
bb3783822f7e0f146fce867ff53eaf49
|
|
| BLAKE2b-256 |
ea78fe6d315d9bd50d434be71d4f2df693d8b84961b7f967b9247c6c3f62707c
|
Provenance
The following attestation bundles were made for jax_bidirectional_tree_rake_compress-0.1.0-py3-none-any.whl:
Publisher:
package.yml on joaospinto/jax-bidirectional-tree-rake-compress
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
jax_bidirectional_tree_rake_compress-0.1.0-py3-none-any.whl -
Subject digest:
71a5683962bd581be6bb35d8b88665f3f716a92e35fea55ba05af5c800f632fb - Sigstore transparency entry: 2211057443
- Sigstore integration time:
-
Permalink:
joaospinto/jax-bidirectional-tree-rake-compress@d62fcf0e43138376d2fff07210b561ec23056ce7 -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/joaospinto
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
package.yml@d62fcf0e43138376d2fff07210b561ec23056ce7 -
Trigger Event:
release
-
Statement type: