Compose category filtering with a requested sum, mean, minimum, or maximum.
One interleaved bag supports twelve requests: three groups combined with four operations. The model must use both root request fields to select the relevant items and infer the requested statistic.
Seed 16 on gpu: 24 of 24 behavioral checks met. See the measurements below.
Insights
The model can answer requests for different groups and operations, but this combined task still needs checks that it uses both requests. The same bag supplies different answers according to visible group and operation requests. Measuring every cell prevents an easy subset from hiding a failed operation or group.
Item coordinates bind categories to values, retained item slots preserve their evidence, and request fields can condition the scalar decoder. The twelve root outputs are learned summaries, not twelve declared statistics. Label and request corruptions have not been applied to this exact route; neighboring proofs do not substitute for them. The evidence supports bounded conditional prediction, with broader relational composition still requiring validation.
Setup
Code
"""Compose a requested group filter with sum, mean, minimum, or maximum.Each bag contains four items from each of three groups and produces all twelverequests. Score every group/operation cell against its own training-meanbaseline so one easy operation cannot hide failure on another.Run this file with --help for seed, training-budget, and reporting options."""from __future__ import annotationsfrom collections.abc import Iteratorfrom functools import partialimport lightning.pytorch as litimport numpy as npimport torchfrom reporting import reportimport relflow as rfPROOF_ID ="P027"OPERATIONS = ("sum", "mean", "min", "max")GROUPS = ("A", "B", "C")
The answer is masked training supervision and omitted at prediction. bag is evaluation metadata, excluded from the model. The same bag also supplies B’s sum (0.6), mean (0.15), and minimum (−0.9) as separate requests.
This is another correctly labeled request for the same bag. The test evaluates each group-operation combination; it does not yet apply a corruption control to this composed task.
Synthetic data and controls
Code
defreduce(values: np.ndarray, operation: str) ->float:"""Execute one synthetic reduction outside RelFlow."""match operation:case"sum":returnfloat(values.sum())case"mean":returnfloat(values.mean())case"min":returnfloat(values.min())case"max":returnfloat(values.max())case _:raiseValueError(f"unknown reduction operation: {operation!r}")def values(rng: np.random.Generator, length: int) -> np.ndarray:"""Draw bounded asymmetric values whose reductions are usually distinct."""if length <4:raiseValueError("reduction bags require at least four values") middle = rng.uniform(-0.2, 0.6, size=length -2) low = rng.uniform(-1.3, -0.7, size=1) high = rng.uniform(0.9, 1.7, size=1)return np.concatenate((middle, low, high))def records(*, bags: int, items_per_group: int, seed: int) -> Iterator[dict]:"""Expand interleaved grouped bags into every group-operation request.""" rng = np.random.default_rng(seed) length =len(GROUPS) * items_per_groupfor bag inrange(bags): bag_values = np.concatenate([values(rng, items_per_group) + rng.uniform(-1.5, 1.5) for _ in GROUPS]) labels = np.repeat(np.asarray(GROUPS), items_per_group) order = rng.permutation(length) items = [{"group": str(labels[index]), "value": float(bag_values[index])} for index in order]for group in GROUPS: selected = bag_values[labels == group]for operation in OPERATIONS:yield {"bag": bag,"selected_group": group,"operation": operation,"items": items,"answer": reduce(selected, operation), }def scores(train: list[dict], test: list[dict], predicted: np.ndarray, keys: tuple[str, ...]) ->dict:"""Normalize each request cell against that cell's training-target mean.""" result = {} cells =sorted({tuple(row[key] for key in keys) for row in test})for cell in cells: mean =float(np.mean([row["answer"] for row in train iftuple(row[key] for key in keys) == cell])) indices = [i for i, row inenumerate(test) iftuple(row[key] for key in keys) == cell] actual = np.asarray([test[i]["answer"] for i in indices]) error =float(np.sqrt(np.mean(np.square(predicted[indices] - actual)))) baseline =float(np.sqrt(np.mean(np.square(mean - actual)))) result["/".join(cell)] = {"rmse": error, "baseline_rmse": baseline, "nrmse": error / baseline}return resultdef predict(model: rf.Model, rows: list[dict]) -> np.ndarray: inputs = [{key: value for key, value in row.items() if key !="answer"} for row in rows] output = model.predict(inputs).to_pylist()return np.asarray([row["predictions"]["request/answer"]["content"] for row in output])
Model tree
Figure 1: All twelve items remain available to a root with twelve learned summaries. Summary slots do not declare particular group-operation results.
The item branch uses reduction=None; the root uses rf.Attention(n_outputs=12, n_layers=2). The twelve learned outputs do not declare twelve semantic group-operation slots.
How it works
Coordinate-local mixing binds groups to values, retained item slots preserve their associations, and visible request fields condition the decoder query. Independently generated bags are assigned to each split before expansion into requests, so related answers stay together. Measuring all twelve cells prevents a favorable average from hiding one failed group or operation.
Add label, selected-group, and operation corruptions to this composed case. Repeat three core seeds and ten calibration seeds, vary collection length, and test absent groups and missing values. Sweep reduction width independently of the number of requested cells. See the filtered-mean control.
Reproduce
Run by stable ID from the repository root:
uv run python proofs/run.py P027
Or run the self-contained script directly:
PYTHONPATH=proofs uv run python proofs/relational/group_operation_composition.py
Add --accelerator gpu for CUDA or --seed 42 for another seeded experiment. --steps 2 checks execution with a short training budget; it is recorded as a smoke run.