CRUX model card

Summary

CRUX is a research project. It tests if rule-conditioned computation and counterfactual learning help a small neural network (about 17M parameters) to decide tasks with rules that the network did not see in training. The release has 11 models with 3 seeds each (33 sets of weights). All models start from jhu-clsp/ettin-encoder-17m (MIT). See NOTICE.

Model Content
A The Ettin-17M encoder with a standard decision head (the baseline)
A-param A with the same trainable parameters as B, without conditioning
A+CF A trained on the paired counterfactual data
B A with an instruction-conditioned low-rank hypernetwork (Rule Compiler), head only
B-abl B with hypernetwork adapters for the top 2 encoder layers
B+CF B trained on the paired counterfactual data
C B+CF with a paraphrase consistency loss
C+cache C with independently cacheable state and rule representations
C+equivariant C with permutation-equivariant candidate scoring
D C with both parts of C+cache and C+equivariant
D-CF D without the paired data and without the consistency loss (a control)

Intended use

Research only. Use the weights to repeat or to check the experiments of the project. Do not use the models for a real decision. They are not reliable on the data of this project (see the results).

Files

weights/<model>/s<k>/model.safetensors holds the model state dict of the best validation checkpoint. It holds no optimizer state. weights/<model>/s<k>/config.yaml holds the config of the run. To load a model, call crux.models.build_model(config["model"]), then call load_state_dict with safetensors.torch.load_file. The code is at https://github.com/0xAnto/crux. The data is at https://huggingface.co/datasets/0xanto/crux-data. MANIFEST.json holds the SHA-256 of every file and the hashes of the splits.

Usage

The models are not transformers models. They need the code of the project. Clone https://github.com/0xAnto/crux, run pip install -r requirements.txt, and run this script in the clone:

# Run in a clone of https://github.com/0xAnto/crux after `pip install -r requirements.txt`.
import torch
import yaml
from huggingface_hub import hf_hub_download
from safetensors.torch import load_file
from transformers import AutoTokenizer

from crux.data.batching import make_dataloader
from crux.models import build_model
from crux.train import input_mode, predict

name, seed = "A", "s0"  # 11 models: A, A-param, A+CF, B, B-abl, B+CF, C, C+cache, C+equivariant, D, D-CF; seeds s0, s1, s2
cfg = yaml.safe_load(open(hf_hub_download("0xanto/crux", f"weights/{name}/{seed}/config.yaml")))
model = build_model(cfg["model"])
model.load_state_dict(load_file(hf_hub_download("0xanto/crux", f"weights/{name}/{seed}/model.safetensors")))
dcfg = yaml.safe_load(open(cfg["data_config"]))  # configs/step2_data.yaml of the clone
tokenizer = AutoTokenizer.from_pretrained(dcfg["tokenizer"])

record = {
    "id": "example-1", "source": "example", "task_type": "binary", "meta": {}, "target": 0,
    "state": "Anne is kind. Anne is young.",
    "instruction": "§1 If a person is kind and young, then this person is happy. Question: Is it established that Anne is happy?",
    "options": ["True", "False", "Unknown"],
}
loader = make_dataloader([record], tokenizer, dcfg, input_mode(cfg["model"]), joint_options=True)
probs = predict(model, loader, torch.device("cpu"), fp16=False)["example-1"]
print({o: round(float(p), 3) for o, p in zip(record["options"], probs)})

A record has a state (facts), an instruction (rules, then the question after Question:), and options. The output is one probability for each option. The output of the script for seed s0 is: A {'True': 0.78, 'False': 0.218, 'Unknown': 0.002}, B {'True': 0.926, 'False': 0.024, 'Unknown': 0.05}, D {'True': 0.28, 'False': 0.258, 'Unknown': 0.462}. To evaluate a model on the data, use python -m crux.benchmark (see the code repository).

Training

  • Base model: jhu-clsp/ettin-encoder-17m (7 layers, hidden size 256, 4 attention heads).
  • Data: the synthetic train split of https://huggingface.co/datasets/0xanto/crux-data (75,646 records).
  • The same budget for all models: 12000 steps of 32 records, 1 trial (learning rate 3e-5, weight decay 0.01 on the weight matrices), AdamW, 6% warm-up, gradient clip 1.0, fp16 mixed precision.
  • Seeds: 13, 42, and 2026 (folders s0, s1, s2).
  • The checkpoint with the best validation unseen_rule_accuracy (evaluation every 1000 steps) is the released checkpoint.
  • Hardware: one NVIDIA L4 (Google Colab) for each run.

Results

The project read the frozen test split one time (Step 8). All values are the mean over 3 seeds. The majority baseline of the test synthetic subset uses the train label as the reference.

Accuracy (higher is better):

Model test synthetic BoardgameQA DynaBench Super-NaturalInstructions
majority baseline 0.336 0.367 0.508 0.442
A 0.640 0.373 0.499 0.440
B 0.636 0.367 0.475 0.434
C 0.513 0.368 0.534 0.441
D 0.442 0.368 0.510 0.433

Counterfactual paired accuracy on test synthetic (higher is better): A 0.481, B 0.472, C 0.327, D 0.241.

The negative result:

  • No architecture is reliably better than A on test synthetic (unseen rules). B equals A on unseen_rule_accuracy (difference -0.004, the 95% CI includes 0) and is 0.009 lower on counterfactual paired accuracy (the CI is below 0).
  • All other models are reliably worse than A. D is 0.198 lower than A.
  • On counterfactual paired accuracy, all models are reliably lower than A.
  • Against the majority baseline, the models are 0.006 below to 0.006 above on BoardgameQA, 0.009 below to 0.001 above on Super-NaturalInstructions, and 0.041 below to 0.037 above on DynaBench (543 records).
  • The models do not transfer from the synthetic data to the ProofWriter open-world sets. The gains over A on these sets come with a large seed spread. They are weak evidence.
  • A is the fastest model: p50 latency 9.53 ms on an L4 at batch size 1 (lower is better). The D family needs about 2 times this latency without a cache.

Limitations

  • The external baselines (Ettin-32M, DeBERTa-v3-xsmall) are not evaluated. There is no comparison with a model that has different pretraining.
  • All 11 models share the Ettin-17M initialization. A contamination affects them equally. The sentence frames and names of the ProofWriter depth5 set occur in the Ettin pretraining data. Super-NaturalInstructions is not scanned. Treat it as possibly seen in pretraining.
  • DynaBench has 543 records, so its CIs are wide.
  • Each model has 3 seeds, 1 trial, and 12000 steps. The bootstrap CI covers the record sample only, not the seed variance. On the ProofWriter sets, the seed spread is larger than most differences.
  • The ProofWriter sets are open-world (True / False / Unknown). Compare them only with published open-world results.
  • No mobile or INT8 measurement exists (Step 9 is skipped).

License

Code, weights, and the synthetic data: Apache-2.0 (LICENSE). NOTICE has the attributions of the third-party parts. DATA_LICENSES.md records the license of each data set.

Citation

@misc{ranjith2026crux,
  author       = {Antony Ranjith F.},
  title        = {Does Rule Conditioning Help a 17M Encoder Decide on Unseen Rules? A Negative Result for the {CRUX} Architectures},
  year         = {2026},
  howpublished = {\url{https://github.com/0xAnto/crux}}
}
Downloads last month

-

Downloads are not tracked for this model. How to track
Inference Providers NEW
This model isn't deployed by any Inference Provider. 🙋 Ask for provider support

Model tree for 0xanto/crux

Finetuned
(44)
this model

Dataset used to train 0xanto/crux