# R Zero Self Evolving Reasoning

> Co-evolutionary framework where Challenger generates tasks and Solver solves them. Models evolve autonomously from scratch without human annotations. Improves math reasoning +6.49pts and general reasoning +7.54pts.

- Skill: `adu2021/r-zero-self-evolving-reasoning` (Agent Skill)
- Install (CLI): `npx skillmds@latest add adu2021/r-zero-self-evolving-reasoning`
- Raw SKILL.md: https://api.skillmd.com/api/skills/adu2021/r-zero-self-evolving-reasoning/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Coding & Dev Tools
- License: MIT
- Author: adu2021 (https://skillmd.com/u/adu2021)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/adu2021/r-zero-self-evolving-reasoning

---


# R-Zero: Self-Evolving Reasoning LLM from Zero Data

## Core Concept

R-Zero eliminates dependency on human-curated task datasets through autonomous co-evolution. Two models—a Challenger that generates progressively harder tasks and a Solver that tackles them—interact in a feedback loop that generates training data and improves both models without any initial labeled dataset. This enables models to break through baseline capabilities through self-generated, curriculum-learned tasks.

## Architecture Overview

- **Challenger Model**: Generates tasks at the frontier of Solver capability
- **Solver Model**: Solves Challenger's tasks and learns from outcomes
- **Co-evolutionary Feedback**: Challenger rewarded for hard-but-solvable tasks; Solver for solving harder tasks
- **Curriculum Learning**: Task difficulty naturally increases as Solver improves
- **Data Generation**: Training data emerges from interaction, not annotation

## Implementation Steps

### Step 1: Initialize Challenger and Solver Models

Set up two models with appropriate initializations and objectives.

```python
import torch
from typing import Dict, List, Tuple

class ChallengerModel(torch.nn.Module):
    """
    Generates tasks at the frontier of solver capability.
    """

    def __init__(self, base_model, task_domain="math"):
        super().__init__()
        self.base_model = base_model
        self.task_domain = task_domain
        self.task_buffer = []
        self.difficulty_estimate = 0.5  # 0-1 scale

    def generate_task(self, difficulty_target: float) -> str:
        """
        Generate a task at target difficulty.

        Args:
            difficulty_target: Target difficulty 0-1 (0=trivial, 1=impossible)

        Returns:
            Task prompt
        """
        prompt = f"""
        Generate a {self.task_domain} problem.

        Difficulty target: {difficulty_target:.1f}/1.0
        - 0.0: Trivial, easily solvable
        - 0.5: Medium difficulty, requires some reasoning
        - 1.0: Very hard, near impossible

        Ensure the problem is clear and has a verifiable solution.
        Problem:
        """

        task = self.base_model.generate(prompt, max_length=300)

        return task

    def estimate_task_difficulty(self, task: str, solver_accuracy: float) -> float:
        """
        Estimate task difficulty based on solver performance.

        Args:
            task: Task description
            solver_accuracy: Proportion of attempts solver succeeds

        Returns:
            Estimated difficulty 0-1
        """
        # Difficulty inversely related to solver accuracy
        estimated_difficulty = 1.0 - solver_accuracy

        return estimated_difficulty

    def compute_reward(self, task: str, solver_solved: bool, solver_accuracy: float) -> float:
        """
        Compute reward for Challenger.

        Challenger rewarded for:
        - Generating tasks Solver cannot solve (high difficulty)
        - But not so hard Solver never solves them (curriculum)

        Args:
            task: Generated task
            solver_solved: Whether Solver succeeded
            solver_accuracy: Solver success rate on similar tasks

        Returns:
            Reward signal for Challenger
        """
        # Target solver success rate around 30-50% (challenging but solvable)
        target_success_rate = 0.4

        difficulty = 1.0 - solver_accuracy

        # Reward is highest when difficulty is at target
        reward = -(difficulty - target_success_rate) ** 2

        return reward
```

### Step 2: Implement Solver Model

Create a model that solves Challenger tasks and provides learning signal.

```python
class SolverModel(torch.nn.Module):
    """
    Solves Challenger's tasks and learns from outcomes.
    """

    def __init__(self, base_model, task_domain="math"):
        super().__init__()
        self.base_model = base_model
        self.task_domain = task_domain
        self.solution_buffer = []
        self.performance_history = []

    def solve_task(self, task: str, max_attempts: int = 1) -> Tuple[str, bool]:
        """
        Attempt to solve a task.

        Args:
            task: Task prompt
            max_attempts: Number of solution attempts

        Returns:
            (solution, is_correct)
        """
        prompt = f"""
        Solve this {self.task_domain} problem:

        {task}

        Show your work and provide final answer.
        Solution:
        """

        solution = self.base_model.generate(prompt, max_length=500)

        # Verify solution (domain-specific)
        is_correct = self.verify_solution(task, solution)

        return solution, is_correct

    def verify_solution(self, task: str, solution: str) -> bool:
        """
        Verify correctness of solution.

        Args:
            task: Original task
            solution: Proposed solution

        Returns:
            True if solution is correct
        """
        verification_prompt = f"""
        Task: {task}
        Proposed solution: {solution}

        Is this solution mathematically correct?
        Respond with only YES or NO.
        """

        response = self.base_model.generate(verification_prompt, max_length=10)

        return "YES" in response.upper()

    def compute_reward(self, task: str, solution: str, is_correct: bool) -> float:
        """
        Compute reward for Solver.

        Solver rewarded for solving increasingly hard tasks.

        Args:
            task: Task that was solved
            solution: Solution provided
            is_correct: Whether solution is correct

        Returns:
            Reward signal for Solver
        """
        if is_correct:
            # Reward based on how hard the task is
            task_difficulty = self.estimate_task_difficulty(task)
            reward = task_difficulty  # Harder tasks = higher reward
        else:
            reward = -0.5  # Penalty for incorrect solution

        return reward

    def estimate_task_difficulty(self, task: str) -> float:
        """Estimate difficulty of a task."""
        # Heuristic: complexity based on task description
        complexity_score = min(len(task.split()), 100) / 100.0
        return complexity_score

    def learn_from_tasks(self, tasks: List[str], batch_size: int = 4):
        """
        Learn from solved tasks.

        Args:
            tasks: List of tasks to train on
            batch_size: Training batch size
        """
        for i in range(0, len(tasks), batch_size):
            batch_tasks = tasks[i:i+batch_size]

            for task in batch_tasks:
                solution, is_correct = self.solve_task(task)

                if is_correct:
                    # Learn from correct solutions
                    self.train_on_solution(task, solution)

    def train_on_solution(self, task: str, solution: str):
        """Fine-tune on task-solution pair."""
        # Standard supervised fine-tuning on correct solution
        prompt = f"Problem: {task}\nSolution:"
        self.base_model.finetune(prompt, solution)
```

### Step 3: Implement Co-evolutionary Training Loop

Create the main loop where Challenger and Solver interact.

```python
class CoevolutionaryFramework:
    """
    Framework orchestrating Challenger-Solver co-evolution.
    """

    def __init__(self, challenger: ChallengerModel, solver: SolverModel):
        self.challenger = challenger
        self.solver = solver
        self.generation = 0

        self.task_archive = []
        self.performance_log = []

    def run_generation(self, num_tasks: int = 20) -> Dict:
        """
        Run one generation of co-evolution.

        Args:
            num_tasks: Number of tasks to generate and solve

        Returns:
            Metrics for this generation
        """
        self.generation += 1

        tasks_generated = []
        tasks_solved_correctly = 0
        challenger_rewards = []
        solver_rewards = []

        # Dynamic difficulty: increase as solver improves
        base_difficulty = self._compute_difficulty_target()

        for task_idx in range(num_tasks):
            # 1. Challenger generates task
            task = self.challenger.generate_task(difficulty_target=base_difficulty)
            tasks_generated.append(task)

            # 2. Solver attempts to solve
            solution, is_correct = self.solver.solve_task(task)

            if is_correct:
                tasks_solved_correctly += 1

            # 3. Compute rewards
            solver_success_rate = tasks_solved_correctly / (task_idx + 1)

            # Challenger reward: task at right difficulty
            challenger_reward = self.challenger.compute_reward(
                task,
                is_correct,
                solver_success_rate
            )
            challenger_rewards.append(challenger_reward)

            # Solver reward: harder tasks give more reward
            solver_reward = self.solver.compute_reward(task, solution, is_correct)
            solver_rewards.append(solver_reward)

            # Store task for learning
            self.task_archive.append({
                "task": task,
                "solution": solution,
                "correct": is_correct,
                "generation": self.generation
            })

        # 4. Learn from generated tasks
        correct_tasks = [t["task"] for t in self.task_archive
                        if t["correct"] and t["generation"] == self.generation]

        self.solver.learn_from_tasks(correct_tasks)

        # 5. Update models via RL
        # (In practice, use policy gradient methods)
        self._update_models_from_rewards(challenger_rewards, solver_rewards)

        # Compute metrics
        metrics = {
            "generation": self.generation,
            "tasks_generated": len(tasks_generated),
            "tasks_solved": tasks_solved_correctly,
            "success_rate": tasks_solved_correctly / num_tasks,
            "avg_challenger_reward": sum(challenger_rewards) / len(challenger_rewards),
            "avg_solver_reward": sum(solver_rewards) / len(solver_rewards),
            "avg_difficulty": base_difficulty
        }

        self.performance_log.append(metrics)

        return metrics

    def _compute_difficulty_target(self) -> float:
        """
        Compute target difficulty for this generation.

        Difficulty increases as solver improves (curriculum learning).

        Returns:
            Difficulty target 0-1
        """
        if not self.performance_log:
            return 0.3  # Start easy

        # Increase difficulty if solver succeeds >50%
        recent_success = self.performance_log[-1]["success_rate"]

        if recent_success > 0.5:
            # Increase difficulty
            new_difficulty = min(1.0, self.performance_log[-1]["avg_difficulty"] * 1.1)
        elif recent_success < 0.2:
            # Decrease difficulty
            new_difficulty = max(0.1, self.performance_log[-1]["avg_difficulty"] / 1.1)
        else:
            new_difficulty = self.performance_log[-1]["avg_difficulty"]

        return new_difficulty

    def _update_models_from_rewards(self, challenger_rewards: List[float], solver_rewards: List[float]):
        """Update models using rewards (simplified)."""
        # In practice, use REINFORCE or other policy gradient methods
        # For now, just update solver on correct tasks
        pass

    def run_coevolution(self, num_generations: int = 10):
        """
        Run full co-evolutionary training.

        Args:
            num_generations: Number of generations to evolve
        """
        for gen in range(num_generations):
            metrics = self.run_generation(num_tasks=20)

            print(f"Generation {gen}: "
                  f"Success Rate={metrics['success_rate']:.1%}, "
                  f"Difficulty={metrics['avg_difficulty']:.2f}")

        return self.performance_log
```

### Step 4: Evaluate Final Performance

Benchmark the evolved model on standard benchmarks.

```python
def evaluate_r_zero_model(solver_model, benchmark_suites: Dict[str, List]) -> Dict:
    """
    Evaluate R-Zero trained model on benchmarks.

    Args:
        solver_model: Evolved solver model
        benchmark_suites: Different benchmark suites (math, reasoning, etc.)

    Returns:
        Performance on each benchmark
    """
    results = {}

    for benchmark_name, problems in benchmark_suites.items():
        correct = 0

        for problem in problems:
            solution, is_correct = solver_model.solve_task(problem["prompt"])
            if is_correct:
                correct += 1

        accuracy = correct / len(problems)
        results[benchmark_name] = accuracy

        print(f"{benchmark_name}: {accuracy:.1%}")

    return results
```

## Practical Guidance

### When to Use R-Zero

- **No human annotations available**: Bootstrapping models from scratch
- **Continuous learning scenarios**: Models that improve by self-play
- **Curriculum learning applications**: Progressive task difficulty needed
- **Research on self-improvement**: Understanding autonomous capability growth

### When NOT to Use R-Zero

- **Abundant labeled data**: Supervised learning likely more efficient
- **Fixed task distribution**: No need for curriculum generation
- **Real-time constraints**: Co-evolution is slow and compute-intensive
- **Safety-critical systems**: Hard to control generated task distribution

### Hyperparameter Recommendations

- **Target solver success rate**: 0.3-0.5 (challenging but solvable)
- **Difficulty increase factor**: 1.05-1.15 per generation
- **Task batch size**: 16-32 tasks per generation
- **Number of generations**: 10-20 for meaningful improvement
- **Tasks per generation**: 20-50 depends on task complexity

### Key Insights

The critical insight is that task generation and solving can bootstrap each other. By rewarding the Challenger for hard-but-solvable tasks, the framework naturally creates a curriculum. The Solver improves by learning from successfully solved tasks, which in turn motivates the Challenger to generate harder problems. This virtuous cycle enables capability growth without human annotation.

## Reference

**R-Zero: Self-Evolving Reasoning LLM from Zero Data** (arXiv:2508.05004)

Introduces co-evolutionary framework with Challenger and Solver models that generate training data and improve autonomously. Demonstrates +6.49pts math and +7.54pts general reasoning improvement without pre-existing labeled data.

