| name | reasoning-core-synthetic-data |
| title | Reasoning Core: A Scalable Procedural Data Generation Suite for Symbolic Pre/Post-Training |
| version | 0.0.2 |
| engine | skillxiv-v0.0.2-claude-opus-4.6 |
| license | MIT |
| url | https://arxiv.org/abs/2603.02208 |
| keywords | ["Synthetic Data","Symbolic Reasoning","Curriculum Learning","Data Generation","Verification"] |
| description | Reasoning Core procedurally generates verifiable symbolic reasoning datasets across formal domains (planning, logic, parsing), with external solvers and curriculum control. |
Technique: Procedural Symbolic Reasoning Dataset Generation
Training language models for reasoning requires large amounts of high-quality data. However, collecting real reasoning data is expensive and limited in scope. Reasoning Core addresses this by procedurally generating symbolic reasoning tasks across five core formal domains: PDDL planning, first-order logic, context-free grammars, causal reasoning, and equation solving.
The key innovation: each generated task includes (1) an external solver that produces verifiable answers, (2) explicit reasoning traces that can be used for supervised training, and (3) difficulty control for curriculum learning. This makes it possible to generate infinite high-quality reasoning training data.
Core Concept
The core insight is that symbolic reasoning domains have well-defined semantics and external solvers. Rather than generating unverifiable reasoning data, generate problems and solutions using domain-specific solvers, then use them for LLM training.
Key properties:
- Verifiable: External solvers guarantee correctness
- Scalable: Generate unlimited data procedurally
- Curriculum-enabled: Difficulty control for progressive training
- Trace-aware: Solutions include reasoning steps, not just answers
- Domain-diverse: Five formal domains covering different reasoning types
Architecture Overview
- Domain-Specific Generators: Procedural problem creation for each domain
- External Solvers: PDDL planner, SAT solver, CYK parser, Bayesian inference, linear algebra
- Trace Extraction: Capture solution reasoning paths
- Difficulty Control: Parametric adjustment of problem complexity
- Reward Functions: Verifiable correctness signals for RL
Implementation Steps
Reasoning Core generates data by sampling problems and solving them. Here's how to implement it:
Implement a procedural problem generator for one domain (e.g., planning):
import random
from typing import List, Dict, Any, Tuple
class PDDLDomainGenerator:
"""Generates PDDL planning problems with controllable difficulty."""
def __init__(self, seed=42):
random.seed(seed)
self.difficulties = []
def generate_problem(
self,
num_objects: int = 5,
num_predicates: int = 10,
plan_length: int = 5,
difficulty: str = 'easy'
) -> Tuple[str, str]:
"""
Generate a PDDL planning problem.
Returns: (domain_str, problem_str) in PDDL format
"""
objects = [f"obj{i}" for i in range(num_objects)]
if difficulty == 'easy':
predicates = self._generate_simple_predicates(objects)
elif difficulty == 'medium':
predicates = self._generate_medium_predicates(objects)
else:
predicates = ._generate_complex_predicates(objects)
goal_state = ._generate_goal(objects, plan_length)
domain = ._format_pddl_domain(predicates)
problem = ._format_pddl_problem(objects, goal_state)
domain, problem
() -> []:
predicates = []
i (, (objects) - , ):
predicates.append({
: ,
: (objects[i], objects[i+])
})
predicates
() -> []:
predicates = []
i, obj (objects):
predicates.append({: , : (obj, )})
i < (objects) - :
predicates.append({
: ,
: (obj, objects[i+])
})
predicates
() -> []:
predicates = []
i, obj (objects):
predicates.append({: , : (obj, )})
j (i+, (i+, (objects))):
predicates.append({
: ,
: (obj, objects[j], )
})
predicates
() -> []:
goal = []
i ((plan_length, (objects))):
goal.append({: , : (objects[i], )})
goal
() -> :
domain =
domain.strip()
() -> :
objects_str = .join(objects)
goal_strs = [ g goal]
goal_clause = .join(goal_strs)
problem =
problem.strip()
Implement a wrapper that generates data with difficulty control:
from dataclasses import dataclass
@dataclass
class ReasoningExample:
"""A reasoning task with solution and reasoning trace."""
problem: str
solution: str
reasoning_trace: List[str]
difficulty: str
domain: str
is_correct: bool
class ReasoningCoreGenerator:
"""Main interface for generating reasoning data."""
def __init__(self):
self.domains = {
'planning': PDDLDomainGenerator(),
}
self.generated_count = 0
def generate_batch(
self,
domain: str,
num_examples: int,
difficulty: str = 'medium',
seed: int = None,
) -> List[ReasoningExample]:
"""
Generate a batch of reasoning examples.
"""
if seed is not None:
random.seed(seed)
examples = []
generator = self.domains.get(domain)
if not generator:
raise ValueError(f"Unknown domain: ")
i (num_examples):
problem_data = generator.generate_problem(difficulty=difficulty)
solution, trace = ._solve_with_external_solver(
domain, problem_data
)
example = ReasoningExample(
problem=(problem_data),
solution=solution,
reasoning_trace=trace,
difficulty=difficulty,
domain=domain,
is_correct=(trace) >
)
examples.append(example)
.generated_count +=
examples
() -> [, []]:
domain == :
solution, trace = ._solve_pddl(problem)
domain == :
solution, trace = ._solve_logic(problem)
:
solution, trace = , []
solution, trace
() -> [, []]:
subprocess
trace = []
solution =
solution, trace
() -> [, []]:
, []
() -> [, [ReasoningExample]]:
split_ratios :
split_ratios = {: , : , : }
dataset = {: [], : [], : []}
difficulty, ratio split_ratios.items():
examples = .generate_batch(
domain,
(num_examples * ratio),
difficulty=difficulty
)
n_train = ((examples) * )
n_val = ((examples) * )
dataset[].extend(examples[:n_train])
dataset[].extend(examples[n_train:n_train+n_val])
dataset[].extend(examples[n_train+n_val:])
dataset
() -> [, ]:
{
: ,
: + .join(example.reasoning_trace) +
,
: {
: example.domain,
: example.difficulty,
: example.is_correct
}
}
Practical Guidance
When to Use:
- Pre-training or post-training language models for reasoning
- When you need large-scale, diverse reasoning data
- For curriculum learning (start easy, progress to hard)
- When you want guaranteed correctness (external solver verification)
When NOT to Use:
- Open-ended generation tasks (not designed for these)
- Real-time data generation (generation happens offline)
- When symbolic reasoning doesn't apply to your domain
Data Generation Strategy:
- Start with 10K examples per domain, scale up based on model size
- Use curriculum: 30% easy, 50% medium, 20% hard
- Generate 5–10x more data than you need; filter by solver confidence
- Mix domains for diverse reasoning skill development
Integration with Training:
- Generate offline dataset using Reasoning Core
- Convert to supervised training format
- Fine-tune model on mixed-domain data
- Optionally use for RL reward signals
Difficulty Control:
- Easy: Single-step reasoning, simple preconditions
- Medium: Multi-step with dependencies
- Hard: Complex constraints, multiple interacting predicates
Results:
- Mixing Reasoning Core data into pre-training improves downstream reasoning
- Preserves or slightly improves general language modeling quality
- Particularly effective for mathematical and symbolic tasks
Reference: Reasoning Core: A Scalable Procedural Data Generation Suite for Symbolic Pre/Post-Training