treeflow.traversal.postorder module
- class treeflow.traversal.postorder.PostorderTopologyData(child_indices: Tensor)
Bases:
object- child_indices: Tensor
- treeflow.traversal.postorder.static_taxon_count(topology) int | None
The taxon count as a Python int if statically known, else
None.topology.taxon_countis prefer_static (derived fromparent_indices’s shape, whichtf.functionpreserves), so it resolves to a concrete value when the node count is known – unlike derived index arrays such aspreorder_node_indices, whose static shape aboolean_maskcan drop even when the input shape is known.
- treeflow.traversal.postorder.postorder_node_traversal(topology: TensorflowTreeTopology, mapping: Callable[[TOutputStructure, TInputStructure, PostorderTopologyData], TOutputStructure], input: TInputStructure, leaf_init: TOutputStructure, xla_compatible: bool = False, unroll: str = 'auto') TOutputStructure
Postorder (children-before-parents) traversal over a fixed topology.
Carries an arbitrary
tf.nestoutput structure per node and appliesmappingat each internal node. General enough to host the phylogenetic likelihood (input= per-node child transition matrices,leaf_init= leaf partials,mapping= the Felsenstein combine step; the rescaled variant can carry(partials, log_scale)as the output structure and sum thelog_scalecomponent afterwards).- Parameters:
topology – The tree topology providing
postorder_node_indicesandchild_indices.mapping –
(child_output, node_input, topology_data) -> node_outputapplied at each internal node, wherechild_outputstacks the children’s outputs on axis 0.input – Per-internal-node input structure, indexed
input[node_index - taxon_count].leaf_init – Per-leaf output structure (leaves on axis 0), used to seed the traversal.
xla_compatible – Only affects the TensorArray paths (
"tensorarray"/"while_loop"). WhenTruethe TensorArray is seeded withunstackand children are read with per-childread+stack; whenFalse(default) it is seeded withscatterand children are read withgather.scatter/gatherhave no XLA kernel, soxla_compatible=Trueis required to XLA-compile the TensorArray forward pass (see theunrollnotes on XLA). It can scale quadratically under XLA, so it is for forward-only XLA use, not a default.unroll –
Which traversal strategy to use – one of
"auto"(default),"unrolled","tensorarray"or"while_loop":"unrolled": a straight-line Python-unrolled graph with no TensorArray. Requires the topology index values to be statically known (tf.get_static_valuefolds them – true for a constant/eager topology). Fastest overall (linear, no per-step loop overhead; one reverse-sweep gradient)."tensorarray": a Python-unrolled loop that writes aTensorArray. Requires only the node count (index shape) to be static; the index values may be runtime tensors (e.g. a topology routed through a JointDistribution)."while_loop": an AutoGraphtf.while_loopover aTensorArray. Requires nothing static; O(1) graph, so it is the right choice when the topology (or its size) varies per call or is very large."auto": pick the fastest feasible –"unrolled"if the values are static, else"tensorarray"if the count is static, else"while_loop".
XLA /
jit_compilenotes (empirically verified):"unrolled"is the only mode that XLA-compiles for value+gradient. Underjit_compilealways use"unrolled"(so a static-value topology), since the TensorArray modes’ backward pass materialises aTensorListthat cannot cross the XLA/TF boundary.The
"tensorarray"/"while_loop"modes XLA-compile only the forward pass, and only withxla_compatible=True(the default scatter/gather ops have no XLA kernel). Their value+gradient never XLA-compiles regardless ofxla_compatible.Wrapping a TensorArray traversal in an inner
tf.functiondoes not shield it from an enclosingjit_compile: the compilation propagates through (even withjit_compile=Falseon the inner function), so it is not an escape hatch for the above.