projenix/tinysynth-reasoning
TinySynth Reasoning Primitives Synthetic training data for teaching small language models stable state representation and controlled reasoning operations — entity/attribute binding, state persistence, mutation, transfer, reference resolution, current-vs-cumulative distinctions, and claim validation — in a systems/computing vocabulary. Every example is generated from a hidden symbolic world and verified by a symbolic solver before any natural language is produced: semantic state… See the full description on the dataset page: https://huggingface.co/datasets/projenix/tinysynth-reasoning.
TinySynth Reasoning Primitives
Synthetic training data for teaching small language models stable state representation and controlled reasoning operations — entity/attribute binding, state persistence, mutation, transfer, reference resolution, current-vs-cumulative distinctions, and claim validation — in a systems/computing vocabulary.
Every example is generated from a hidden symbolic world and verified by a symbolic solver before any natural language is produced:
semantic state schema -> symbolic world -> symbolic events -> solver
-> known final + intermediate states -> structural validation -> renderingLanguage only ever renders already-verified truth; it never determines the answer. Nouns and verbs are not mixed freely — each state variable owns the verbs that may act on it, so semantically broken text like "3 stored files finish" is unproducible by construction rather than filtered out afterwards.
Configs = curriculum levels
One config per difficulty level, intended to be trained in order.
Difficulty is computed from latent features (transitions, entities, references, distractors, no-ops, conditions), not from sentence count — a long arithmetic chain does not automatically score high.
Usage
from datasets import load_dataset
ds = load_dataset("sam/tinysynth-reasoning", "difficulty_0")
print(ds["train"][0]["text"])Train on the text field. To walk the curriculum:
for level in range(6):
ds = load_dataset("sam/tinysynth-reasoning", f"difficulty_{level}")
train(ds["train"])Recover the symbolic ground truth for analysis or programmatic grading:
import json
latent = json.loads(ds["train"][0]["latent_state"])
# {'initial': {...}, 'events': [...], 'final': {...}, 'query': {...}}Splits
Splits are assigned per latent problem, so every surface variant of a problem stays in one split — there is no leakage between splits even though the same underlying world may be rendered several ways.
Fields
latent_state is a JSON string rather than a nested struct on purpose: its keys are entity-specific (host_1.running_containers, ...), so an inferred Arrow struct would carry hundreds of mostly-null columns and would not be stable across shards or datasets versions.
Generation
python -m tinysynth.hf_export --output-dir tinysynth-reasoning \
--target-tokens-per-level 20000000 \
--seed 42 --variants-per-problem 3 \
--ood-fraction 0.05Generation is deterministic for a fixed seed and configuration.
Design notes and limitations
- Observation is not mutation. A large share of the corpus trains explicitly on the fact that checking, reading, listing, or displaying state leaves it unchanged, including minimal pairs (
contrast_group_id) whose setups are identical apart from one verb. - Similar nouns are distinguished.
queued_requestsandtotal_requests_receivedare different variables; an arrival increments both, a completion decrements only the first. - Anaphora appear only where the antecedent is adjacent and unambiguous, so hard coreference is out of scope by design.
- Conditionals are single-clause threshold checks; there is no nested depth.
- Numbers are small (0-9 by default) on purpose — the target is reasoning mechanics, not arithmetic difficulty.
- The
ood_testsplit varies surface realization only, not reasoning structure; it measures template memorization, not compositional generalization.
