Can it distinguish an unknown set from an empty set?

Vocabulary
Predict whether a set is nonempty, including sets whose member names were absent from training.

Example

tags: [tag-12]
nonempty: 1.0

The answer is shown here for explanation; it is hidden from the model when scored.

Model tree with record, tags, nonempty. Amber cards are hidden prediction targets. Other fields provide the input.

Model tree with record, tags, nonempty. Amber cards are hidden prediction targets. Other fields provide the input.

Figure 1: Amber cards are hidden prediction targets. Other fields provide the input.

Comparison

Compare training with input members sometimes made unavailable against training that never exposes this condition. Repeat unknown members to check duplicates.

Result

P064 · Latest full run · Passing

Seed 7641 on gpu: 8 of 8 test checks met. History: 7 of 9 full runs met every check, across 4 distinct seeds. History can include earlier source versions. The experiment code has changed since this run; these measurements describe its earlier version.

Comparison Latest measurement
Unknown-input error, fallback trained 0.006947
Unknown-input error, fallback untrained 0.7017

The trained fallback distinguishes unknown nonempty input from empty input. It preserves the presence of members, rather than learning their individual meanings.

Experiment details and code

Latest full run

Seed 7641, gpu, recorded 2026-09-18T16:55:05.716590+00:00. Outcome: met.

Source fingerprint: aed288b38692b629cecdd21199bfc1d8efc100aa02f63bab4cd0678ae36412ae. Python 3.12.6; Torch 2.12.0.

Measurement Value
constant_rmse 0.5
control_known 0.0113686
control_unknown 0.70173
control_duplicate_and_identity_drift 0
augmented_known 0.00661856
augmented_unknown 0.00694725
augmented_duplicate_and_identity_drift 0
Behavioral checks
Behavioral check Outcome
control_known: RMSE below 0.10 Met
control: unknown spelling and duplicates are invariant Met
control: unknown evaluation leaves vocabulary frozen Met
augmented_known: RMSE below 0.10 Met
augmented_unknown: RMSE below 0.10 Met
augmented: unknown spelling and duplicates are invariant Met
augmented: unknown evaluation leaves vocabulary frozen Met
Trained fallback improves unknown-input RMSE by more than 0.40 Met

Full run history

Runs may include earlier source versions. Repeating a seed is not an independent seed check.

Seed Outcome Recorded Code fingerprint
7699 met 2026-09-17T00:44:15.616735+00:00 7a0cd52374273b96c7bf7d236cb5c7b3f22e26e32e4deeda4d4a1417126e7330
7641 met 2026-09-17T00:50:44.263711+00:00 7a0cd52374273b96c7bf7d236cb5c7b3f22e26e32e4deeda4d4a1417126e7330
7641 not met 2026-09-17T00:55:03.400469+00:00 7a0cd52374273b96c7bf7d236cb5c7b3f22e26e32e4deeda4d4a1417126e7330
7641 met 2026-09-17T00:57:03.562363+00:00 7a0cd52374273b96c7bf7d236cb5c7b3f22e26e32e4deeda4d4a1417126e7330
7701 met 2026-09-17T00:57:56.329891+00:00 7a0cd52374273b96c7bf7d236cb5c7b3f22e26e32e4deeda4d4a1417126e7330
7702 met 2026-09-17T00:58:49.095431+00:00 7a0cd52374273b96c7bf7d236cb5c7b3f22e26e32e4deeda4d4a1417126e7330
7641 not met 2026-09-18T16:29:47.355970+00:00 7a0cd52374273b96c7bf7d236cb5c7b3f22e26e32e4deeda4d4a1417126e7330
7641 met 2026-09-18T16:44:01.039062+00:00 7a0cd52374273b96c7bf7d236cb5c7b3f22e26e32e4deeda4d4a1417126e7330
7641 met 2026-09-18T16:55:05.716590+00:00 7a0cd52374273b96c7bf7d236cb5c7b3f22e26e32e4deeda4d4a1417126e7330

Recorded results.

Run this experiment

Proof execution is optional and separate from building this site. Use a RelFlow build matching the recorded provenance; replace the wheel path below with that artifact. Run from this documentation repository’s root:

uv run --with /path/to/matching-relflow.whl python proofs/run.py P064

Or, from a prepared proof environment, run the self-contained script directly:

PYTHONPATH=proofs python proofs/vocabulary/set_unavailable_fallback.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.

Download the complete proof.

Training and controls

Each arm trains for 400 AdamW updates on 8,192 rows, with 512 independent validation rows. Evaluate 2,048 known and disjoint-label rows. The constant 0.5 predictor has RMSE 0.5. Both arms must learn familiar sets (RMSE < 0.10). Only the augmented arm must transfer to unknown sets (RMSE < 0.10); its gain over the untrained-fallback control must exceed 0.40 RMSE. Duplicate and unknown-spelling changes must leave predictions unchanged.

Complete experiment code

Code
"""P064: input corruption teaches an unavailable Set representation."""

from collections.abc import Iterator
from functools import partial

import lightning.pytorch as lit
import numpy as np
import pyarrow as pa
from reporting import report

import relflow as rf

PROOF_ID = "P064"
BUDGET = 400


def records(*, rows: int, seed: int, novel: bool = False) -> Iterator[dict]:
    rng = np.random.default_rng(seed)
    for label, nonempty in zip(rng.integers(32, size=rows), rng.random(rows) < 0.5, strict=True):
        yield {"tags": [f"{'novel' if novel else 'tag'}-{label}"] if nonempty else [], "nonempty": float(nonempty)}


def build(p_unavailable: float) -> rf.Model:
    model = rf.Model(
        d_model=32,
        n_layers=1,
        n_heads=4,
        batch_size=64,
        dropout=0.0,
        tags=rf.Set(p_unavailable=p_unavailable),
        nonempty=rf.Number(mask=True, objective="mse"),
    )
    model.optimizer = rf.adamw(learning_rate=0.002)
    return model


def predict(model: rf.Model, rows: list[dict]) -> np.ndarray:
    return np.asarray(
        [row["nonempty"]["content"] for row in model.predict(pa.Table.from_pylist(rows))["predictions"].to_pylist()]
    )


def run(seed: int, steps: int | None, accelerator: str) -> tuple[dict, dict]:
    metrics, checks = {"constant_rmse": 0.5}, {}
    for probability in (0.0, 0.5):
        lit.seed_everything(seed, workers=True)
        model = build(probability)
        data = rf.SyntheticDataModule(
            model=model,
            train=partial(records, rows=8192, seed=seed + 1),
            validate=partial(records, rows=512, seed=seed + 2),
            seed=seed,
        )
        trainer = lit.Trainer(
            accelerator=accelerator,
            devices=1,
            max_epochs=-1,
            max_steps=BUDGET if steps is None else min(steps, BUDGET),
            deterministic=True,
            logger=False,
            enable_checkpointing=False,
            enable_progress_bar=False,
            enable_model_summary=False,
            num_sanity_val_steps=0,
        )
        trainer.fit(model, datamodule=data)
        vocabulary = rf.Set.vocabulary(model, "/tags")
        arm = "augmented" if probability else "control"
        for novel in (False, True):
            rows = list(records(rows=2048, seed=seed + 3, novel=novel))
            expected = np.asarray([row["nonempty"] for row in rows])
            values = predict(model, rows)
            key = f"{arm}_{'unknown' if novel else 'known'}"
            metrics[key] = float(np.sqrt(np.mean((values - expected) ** 2)))
            if not novel or probability:
                checks[f"{key}: RMSE below 0.10"] = metrics[key] < 0.10
            if novel:
                changed = [{**row, "tags": ["another", "another"] if row["tags"] else []} for row in rows]
                drift = float(np.max(np.abs(values - predict(model, changed))))
                metrics[f"{arm}_duplicate_and_identity_drift"] = drift
                checks[f"{arm}: unknown spelling and duplicates are invariant"] = drift < 1e-5
        checks[f"{arm}: unknown evaluation leaves vocabulary frozen"] = rf.Set.vocabulary(model, "/tags") == vocabulary
    checks["Trained fallback improves unknown-input RMSE by more than 0.40"] = (
        metrics["control_unknown"] - metrics["augmented_unknown"] > 0.40
    )
    return metrics, checks


if __name__ == "__main__":
    report(PROOF_ID, run, seed=7641)