diff --git a/reasoning_gym/algorithmic/__init__.py b/reasoning_gym/algorithmic/__init__.py index b54511f1..0b20bbca 100644 --- a/reasoning_gym/algorithmic/__init__.py +++ b/reasoning_gym/algorithmic/__init__.py @@ -7,7 +7,7 @@ Algorithmic tasks for training reasoning capabilities: """ from .ab import ABConfig, ABDataset -from .base_conversion import BaseConversionConfig, BaseConversionDataset +from .base_conversion import BaseConversionConfig, BaseConversionCurriculum, BaseConversionDataset from .binary_alternation import BinaryAlternationConfig, BinaryAlternationCurriculum, BinaryAlternationDataset from .binary_matrix import BinaryMatrixConfig, BinaryMatrixCurriculum, BinaryMatrixDataset from .caesar_cipher import CaesarCipherConfig, CaesarCipherDataset @@ -46,6 +46,7 @@ __all__ = [ "SpellBackwardDataset", "BaseConversionConfig", "BaseConversionDataset", + "BaseConversionCurriculum", "CaesarCipherConfig", "CaesarCipherDataset", "CryptarithmConfig", diff --git a/reasoning_gym/algorithmic/base_conversion.py b/reasoning_gym/algorithmic/base_conversion.py index 5ab5b5b5..f3b9d6a1 100644 --- a/reasoning_gym/algorithmic/base_conversion.py +++ b/reasoning_gym/algorithmic/base_conversion.py @@ -4,6 +4,7 @@ from dataclasses import dataclass from random import Random from typing import Optional +from ..coaching import AttributeType, BaseCurriculum, RangeAttributeDefinition from ..factory import ProceduralDataset, register_dataset QUESTION_TEMPLATE = """Your task is to convert a number between two different bases. @@ -108,8 +109,41 @@ class BaseConversionDataset(ProceduralDataset): "target_base": target_base, "source_repr": source_repr, "target_repr": target_repr, + "difficulty": { + "value": value, + "base": (source_base, target_base), + }, }, } -register_dataset("base_conversion", BaseConversionDataset, BaseConversionConfig) +class BaseConversionCurriculum(BaseCurriculum): + def __init__(self): + super().__init__(BaseConversionCurriculum.__name__, BaseConversionConfig) + + # Define attributes + self._define_attributes( + RangeAttributeDefinition( + name="base", + levels=[9, 18, 27, 36], + default_level=0, + description="The base of the number system", + attr_type=AttributeType.APPEND, + min_value=2, + lower_field_name="min_base", + upper_field_name="max_base", + ), + RangeAttributeDefinition( + name="value", + levels=[1_000, 10_000, 100_000, 1_000_000], + default_level=0, + description="The value to convert", + attr_type=AttributeType.APPEND, + min_value=0, + lower_field_name="min_value", + upper_field_name="max_value", + ), + ) + + +register_dataset("base_conversion", BaseConversionDataset, BaseConversionConfig, BaseConversionCurriculum) diff --git a/tests/test_base_conversion.py b/tests/test_base_conversion.py index 8017d74a..1e0ebe55 100644 --- a/tests/test_base_conversion.py +++ b/tests/test_base_conversion.py @@ -2,7 +2,11 @@ import pytest -from reasoning_gym.algorithmic.base_conversion import BaseConversionConfig, BaseConversionDataset +from reasoning_gym.algorithmic.base_conversion import ( + BaseConversionConfig, + BaseConversionCurriculum, + BaseConversionDataset, +) def test_base_conversion_config_validation(): @@ -163,3 +167,28 @@ def test_base_conversion_formatting(): assert item["answer"].strip() == item["answer"] # Verify hint is included for bases > 10 assert "use lowercase letters" in item["question"] + + +def test_base_conversion__curriculum(): + curriculum = BaseConversionCurriculum() + + base_value = {"size": 150, "seed": 1} + + base_cfg: BaseConversionConfig = curriculum.generate_configuration(base_value) + assert base_cfg.seed == 1 + assert base_cfg.size == 150 + assert base_cfg.min_base == 9 and base_cfg.max_base == 9 + assert base_cfg.min_value == 1000 and base_cfg.max_value == 1000 + + # test incrementing attribute levels + curriculum.increment_attr_level("base") + curriculum.increment_attr_level("value") + increased_cfg = curriculum.generate_configuration(base_value) + assert increased_cfg.min_base == 9 and increased_cfg.max_base == 18 + assert increased_cfg.min_value == 1000 and increased_cfg.max_value == 10000 + + # test decrementing attribute level for base again + curriculum.decrement_attr_level("base") + partially_decreased_cfg = curriculum.generate_configuration(base_value) + assert partially_decreased_cfg.min_base == 9 and partially_decreased_cfg.max_base == 9 + assert partially_decreased_cfg.min_value == 1000 and partially_decreased_cfg.max_value == 10000