CoolFace
Datasetpublic

ksopyla/cogito-probe-arith

CogitoProbe-Arith: nested arithmetic with mixed brackets Synthetic nested + - * expressions with mixed brackets ()[]{}. Three question types: the final number (eval, an easy shortcut), internal-node values (subexpr, the real test), and which closer matches an opener (match). Use it to test whether a model stored the tree, not just a calculator. Author: Krzysztof Sopyła · License: Apache-2.0 · Seed: 20260916 · Tokenizer: HuggingFaceTB/SmolLM3-3B In 60 seconds… See the full description on the dataset page: https://huggingface.co/datasets/ksopyla/cogito-probe-arith.

sourceHugging Faceapache-2.0updated 7d agoView on Hugging Face
0likes78downloads
Dataset Card

CogitoProbe-Arith: nested arithmetic with mixed brackets

Synthetic nested + - * expressions with mixed brackets ()[]{}. Three question types: the final number (eval, an easy shortcut), internal-node values (subexpr, the real test), and which closer matches an opener (match). Use it to test whether a model stored the tree, not just a calculator.

Author: Krzysztof Sopyła · License: Apache-2.0 · Seed: 20260916 · Tokenizer: HuggingFaceTB/SmolLM3-3B

In 60 seconds

Several independent expressions are written up front, then filler, then a question about the first (farthest) expression.

`task`What it asksTreat as
evalQ eval → the root numberControl only. One integer; a calculator shortcut.
subexprQ sub 0 . 2 . → values of internal nodesPrimary. Needs the expression tree.
matchQ match <opener-index> → matching closer indexPrimary. Bracket matching, not arithmetic.

Mixed ()[]{} do not change the numeric value. They colour the brackets so matching is a real question. Do not report eval accuracy as “the model understands arithmetic.”

Load it

python
from datasets import load_dataset

ds = load_dataset("ksopyla/cogito-probe-arith")
row = ds["validation"][0]

print(row["seq_len"], row["task"], row["variant"])
print("query: ", row["query"])
print("answer:", row["answer"])
print("prize bits:", row["prize_bits"], "gap:", row["gap"])

# Loss only on the answer span (already marked).
# input_ids / labels are lists of int, length == seq_len.
loss_tokens = [t for t in row["labels"] if t != -100]

# Start small on a laptop: 1,024-token rows, fixed fact count.
small = ds.filter(lambda r: r["seq_len"] == 1024 and r["variant"] == "fixed")

You can ignore input_ids and train from context / query / answer as text. If you do use the provided ids, they are already tokenized for HuggingFaceTB/SmolLM3-3B (Llama-3 vocab) and must not be re-tokenized.

The four CogitoProbe datasets

DatasetJob in one lineTypical use
`ksopyla/cogito-probe-bits`Recall values for keys buried in a haystackMemory / retrieval / compression capacity
`ksopyla/cogito-probe-bind`Who has which colour, who lives where, friend's cityCompositional binding vs bag-of-words
`ksopyla/cogito-probe-arith`Nested arithmetic + bracket matchingDid it store the expression tree?
`ksopyla/cogito-probe-props`Object colours amid fluent fillerFacts vs padding statistics

Length ladder (every family): 1024 → 4096 → 8192 → 16384 → 32768. Half the rows are fixed (same fact count as at 1k, longer haystack), half are scaled (more facts as the row grows).

Why these exist

Web text is locally predictable: a language model can look strong by guessing nearby words without remembering a fact from thousands of tokens earlier. These four datasets hide a known set of facts in a long padded haystack so you can measure whether a model (or a small latent memory) actually stored them.

Each row tells you how many bits the answer is worth (prize_bits) and how far the question sits from the last fact (gap). That is the whole point: the information content is labelled, the distractor text is not the prize, and the length is a ladder rather than a single context size.

Real rows use random single-token English-ish pieces from the Llama-3 / SmolLM3 vocabulary (gonzalez, oslo, validators, …), not the toy names alice / bob in the examples above. The grammar of the task is the same.

How to score

Train or evaluate only on the answer span. Teacher-forced token accuracy on labels != -100 is the main number. Recovered bits against the labelled prize:

max(0, prize_bits + Σ log2 p(gold_t))

A decoder that cannot see tokens more than gap away must sit at chance — the evidence is that far from the answer.

Score subexpr and match as the real tasks. Score eval only as a shortcut control (~one integer). If only eval moves, the model is a calculator, not a structure memory. Digit strings are space-separated (3 9 for 39, - 7 for −7).

Schema

columnmeaning
text / context / query / answerReadable surfaces. text is the full padded row.
input_ids, attention_mask, labelsReady for causal LM training. labels is -100 everywhere except the answer.
seq_len, rungPadded length: 1024, 4096, 8192, 16384, or 32768.
variantfixed = same number of facts as at 1k, longer haystack. scaled = more facts as the row gets longer.
taskQuestion type inside this family (see above).
prize_bitsKnown information content of the gold answer (combinatorial lower bound).
gapTokens from the last evidence token to the start of the answer.
answer_start / answer_end / evidence_endCharacter-free token indices into input_ids.
metaJSON string: fact table, fingerprints, node values.

This build

splitrows
train8448
validation896
test896
metricvalue
total rows10240
token length (all padded)min 1024 / p50 4096.0 / max 32768
mean prize bits12.914 (min 3.322, max 30.546)
mean gzip ratio (text)0.026 (n=160 stratified sample)
mean gzip ratio (int32 ids)0.037
mean unigram entropy (bits)6.184
mean bigram entropy (bits)6.286
answer entropy (bits)8.604 over 2612 strings
tasks{'eval': 2037, 'subexpr': 5175, 'match': 3028}
variants{'scaled': 5120, 'fixed': 5120}
rungs{'seq1024': 4608, 'seq4096': 2560, 'seq8192': 1280, 'seq16384': 1024, 'seq32768': 768}

Per-rung means:

seq_lennmean prize bitsmean gapmean gzip(text)
1024460811.5761001.90.068
4096256012.0864074.20.023
8192128015.5488168.90.016
16384102415.30316360.70.012
3276876816.12932744.70.010

Example rows (truncated):

  • arith/seq1024/scaled/train/00000 task=eval prize=6.64 bits gap=1004 query=Q eval answer=3 9
  • arith/seq1024/scaled/train/00001 task=subexpr prize=15.27 bits gap=1005 query=Q sub 2 . 1 . answer=1 8 . 1 4
  • arith/seq1024/scaled/train/00002 task=subexpr prize=15.27 bits gap=988 query=Q sub 5 . 2 . answer=- 1 . - 1 2

Split leakage

pairfingerprint overlapinput_ids overlaptext overlapanswer-string overlap
train∩validation000248
train∩test000245
validation∩test00095

Within-split duplicate input_ids counts: {'train': 0, 'validation': 0, 'test': 0}.

Train / validation / test use disjoint random streams. A fingerprint of the facts is checked for overlap. Shared answer strings (for example the same 8 colours) are expected and are not a leak.

Rebuild

Deterministic rebuild (does not upload):

bash
uv run python scripts/build_concept_probe_datasets.py \
  --scale full --seed 20260916 \
  --tokenizer HuggingFaceTB/SmolLM3-3B \
  --families arith \
  --out_dir Cache/concept_probes/full

Ids are composed from a verified 1-token atom table of HuggingFaceTB/SmolLM3-3B. Arithmetic rows inject bare digit and bracket ids; they do not BPE-encode a glued string such as (1+2)*[3-4] (that merge path is not a well-defined alphabet).

Limitations

  • Not natural language. Atoms are verified 1-token pieces of the SmolLM3 / Llama-3 vocab, chosen so each symbol is one id. Do not treat this as a human corpus.
  • Answers are packed (several values in one span). Single-token labels are too sparse for a small latent channel to learn from.
  • prize_bits is a counting lower bound on the answer, not a cross-entropy floor of a local language-model window.
  • Arithmetic mixed brackets colour the tree; they do not change + - * meaning. eval-only accuracy is not evidence of rich structure.
  • Rows are padded with a repeating filler cycle, so gzip of the full text looks tiny. Compare prize_bits, not compressibility of the padded row.

Origin

These files were built for a research project on compressing long context into a small set of latent vectors (“concepts”), so the author could ask what those vectors actually store. You do not need that project, its training code, or its internal experiment log to use the datasets.

Project page: ai.ksopyla.com · author: Krzysztof Sopyła. Generator: data/concept_probes/ in the public research repo (MIT).

License

Apache-2.0 for this synthetic dataset. No web scrapes, no personal data. Generator code is MIT.

Citation

@misc{cogitoprobe2026,
  title  = {CogitoProbe: synthetic long-haystack probes for memory and compression},
  author = {Sopyła, Krzysztof},
  year   = {2026},
  url    = {https://huggingface.co/datasets/ksopyla/cogito-probe-arith},
  note   = {Seed 20260916. Four families: bits, bind, arith, props.},
}