Branch
A payment of 84.50 at North Market may be easier to assess alongside the account’s earlier activity. A Branch keeps the earlier events and latest payment in one sequence, with each timestamp, amount, and merchant together. The model can learn relationships within an event and across the sequence.
import relflow as rf
model = rf.Model.xs(
trxns=rf.Branch(
length=32,
overflow="tail",
booked_at=rf.DateParts(
dateparts=["day_of_month", "day_of_week", "hour_of_day"],
),
amount=rf.Number,
merchant=rf.Category,
disputed=rf.Boolean(mask=True),
),
)One account review observation, ordered oldest to latest:
trxns:
- booked_at: "2026-05-21T10:00:00"
amount: 32.00
merchant: Station Cafe
disputed: false
- booked_at: "2026-05-27T18:15:00"
amount: 56.20
merchant: North Market
disputed: true
- booked_at: "2026-05-28T14:30:00"
amount: 84.50
merchant: North Market
disputed: falselength=32 keeps at most 32 transactions. overflow="tail" keeps the most recent ones, including the latest payment, when the application supplies this order. A preprocessor owns sorting. mask=True hides and predicts the dispute label for every transaction, and prediction records omit it. Prepare observations defines the cutoff and label window these records follow.
Binding and shape
The parent keyword supplies the collection name. With query=None, trxns reads the same-named child collection, and its leaves read keys within each transaction. Use a query for a different structural path and a preprocessor for sorting, filtering, or derived values.
A child branch requires a list of objects, including when length=1. Only the generated model root reads one object directly. Each child branch adds a repeated axis. Its execution width follows the selected collections in each batch; an explicit length caps retained items. Padding does not contribute to attention or reduction, but it still has storage and processing costs. Prediction output writes every record collection as a variable Arrow list of sibling result structs, without public cap padding. Structural nulls and empty collections are preserved.
Definitions are reusable: binding clones them without changing the original. Use fields={"length": rf.Number} for generated schemas or child names that collide with configuration. Use Set for membership alone and Vector for one numeric vector.
Automatic lengths and limits
Omitting length, or setting length=None, retains every collection slot:
history = rf.Branch(
amount=rf.Number,
merchant=rf.Category,
)Each batch uses the largest retained collection length, including null object slots. Widths can grow or shrink between batches; all-empty collections have width zero. Nested widths are resolved after any parent limit has selected its records. Leaves beneath the same branch share its geometry, independently of values, masking, or which target is supplied. This changes execution geometry, without updating the schema, growing parameters, or replacing the optimizer.
Set a positive integer length for a hard limit. Only bounded branches accept overflow="head", "tail", or "error"; omission or None selects "head" when a limit exists. Branch(overflow="tail", ...) without a finite length is an error. The generated root always remains a singleton with length=1 and internal overflow="error".
Bounded branches also pad only to the largest retained length in each batch, up to their cap. This internal execution width does not add padding to public record collections.
With length=None, untrusted requests control nested collection sizes during inference. Oversized collections can exhaust CPU/GPU memory or compute and deny service to other users. Requests from multiple sources can amplify this into a distributed denial-of-service (DDoS) attack.
Set finite branch lengths and enforce request-size limits before public serving. A batch-size limit alone does not bound nested collections. RelFlow logs one aggregate warning per model generation for active unbounded branches; the warning enforces no limit. No hidden cap is applied to length=None.
Options
These are the direct rf.Branch defaults. A size preset such as Model.xs fills omitted branch settings; explicit node settings take precedence.
| Option | Default | Meaning |
|---|---|---|
fields |
None |
Named child mapping, combinable with keyword children; names must be unique. |
query |
None |
Optional node-relative structural path. |
length |
None |
Automatic batch width; a positive integer caps retained items per parent coordinate. |
overflow |
None |
Unbounded branches require None. With finite length, omission becomes "head"; choose "head", "tail", or "error". Order comes from the processed source. |
attention |
"mha" |
Encoder mode; see below. |
n_layers |
1 |
Positive sequence-encoder depth. |
n_heads |
4 |
Positive, even encoder head count and fallback for Attention reduction. Active attention requires heads to divide d_model with at least two dimensions per head. |
ffnx |
8 |
Positive integer feed-forward multiplier for this branch’s encoders and learned reduction. See model-tree configuration. |
reduction |
rf.Attention() |
What encoded memory reaches the parent. |
dropout |
None |
Optional encoder dropout in [0, 1) and reduction fallback. |
mask |
False |
Visibility and reconstruction policy applied atomically to active descendants. |
embed |
False |
Export this branch’s contextual embedding. |
description |
None |
Human-readable notes. |
Branch settings are local. Model settings configure the generated singleton root; they do not replace every child branch’s defaults.
Attention and reduction
An attention-enabled branch first mixes direct sibling leaves at each aligned record coordinate, when there is more than one. Its n_layers sequence stack then mixes child slots across the branch. Finally, reduction chooses the representations routed to its parent.
| Encoder mode | Key/value heads |
|---|---|
"mha" |
One per query head. |
"gqa" |
One per pair of query heads. |
"mqa" |
One shared by every query head. |
None |
No coordinate or sequence attention; reduction still runs. |
Sharing key/value heads changes model capacity as well as cost.
| Reduction | Parent receives | Tradeoff |
|---|---|---|
rf.Attention(n_outputs=1) |
A fixed number of learned summaries. | Bounded memory; outputs do not guarantee item identity. |
rf.Mean() |
One average of present encoded vectors plus a learned count lane. | Cheap; multiplicity survives through the count lane, while order and individual values are not directly preserved. |
None |
Every encoded child slot with its presence state. | Keeps token correspondence available; increases parent sequence cost. |
Attention has n_outputs=1, n_layers=1, n_heads=None, dropout=None, and position=True. Counts are positive. Omitted heads and dropout inherit from the branch; explicit values override them. position controls rotary position in the reducer only. Mean has no tuning options. These configurations are frozen and share settings, not learned weights, when reused.
attention=None and reduction=None skip different operations. Use both for pure pass-through. Turning off reducer position alone does not remove position information from earlier encoder attention.
Choose reduction from the evidence the parent needs. A mean is not a raw-field sum, and more learned summaries do not perform a join. Capacity must survive later reductions too. model.schema.branch_outputs reports each branch’s static routed width, or None when pass-through width depends on the batch.
The nested cardinality proof tests whether session summaries preserve enough information for their parent. The Mean control isolates what is lost when identical repeated values become one normalized summary.
Masks and output
disputed=rf.Boolean(mask=True) keeps the answer out of the inputs and produces a dispute prediction for every transaction. The predictions retain their transaction positions; the branch groups those results and has no typed prediction value of its own.
embed=True exports branch context at its address. See Reusable representations for output roles and Model tree for the route through nested contexts.