reasoning-gym/reasoning_gym/curricula/algorithmic/base_conversion_curriculum.py
2025-02-09 02:18:11 +00:00

135 lines
No EOL
5.3 KiB
Python

"""
Curriculum definition for base conversion exercises.
"""
from typing import Dict, Any
from reasoning_gym.core.base_curriculum import BaseCurriculum
from reasoning_gym.core.attributes import AttributeDefinition, AttributeType
from reasoning_gym.core.template import Template
class BaseConversionCurriculum(BaseCurriculum):
def __init__(self):
super().__init__("BaseConversionCurriculum")
def _init_curriculum(self) -> None:
"""Initialize the base conversion curriculum configuration"""
# Define valid attribute types
self._valid_types = {
AttributeType.STATIC, # For base names
AttributeType.UBOUND, # For ranges like value, base
AttributeType.APPEND # For accumulating options
}
# Define attributes
self._attributes = {
"value": AttributeDefinition(
levels=[100, 1000, 10000],
default_level=0,
description="Maximum decimal value to convert",
attr_type=AttributeType.UBOUND,
min_value=1
),
"base_range": AttributeDefinition(
levels=[16, 26, 36],
default_level=0,
description="Maximum base value (2 is minimum)",
attr_type=AttributeType.UBOUND,
min_value=2 # Ensure at least binary
),
"base_names": AttributeDefinition(
levels=[{"2": "binary", "16": "hexadecimal"},
{"8": "octal", "10": "decimal"}],
default_level=0,
description="Special names for bases",
attr_type=AttributeType.APPEND
),
"hint": AttributeDefinition(
levels=[True, False],
default_level=0,
description="Whether to include a hint",
attr_type=AttributeType.STATIC
)
}
# Define templates with symbolic placeholders
self._templates = [
Template(
template="Convert {source_value} from {source_base} to {target_base}",
parts={
"source_value": "value",
"source_base": "base_src",
"target_base": "base_trg"
}
),
Template(
template="What is {source_value} ({source_base}) in {target_base}",
parts={
"source_value": "value",
"source_base": "base_src",
"target_base": "base_trg"
}
),
Template(
template="Express the {source_base} number {source_value} in {target_base}",
parts={
"source_value": "value",
"source_base": "base_src",
"target_base": "base_trg"
}
)
]
# Define symbolic structure
self._symbolic = {
# Define shared variables that need to be consistent
"shared_vars": {
"src_base": lambda refs: refs["base_range"]()
},
"generators": {
"trg_base": lambda refs: (
base := refs["base_range"](),
base if base != refs["src_base"](refs) else refs["trg_base"](refs)
)[-1],
"format_base": lambda refs: lambda base: (
names := refs["base_names"](),
names.get(str(base), f"base-{base}") if refs["dataset_rng"].random() < 0.5 else f"base-{base}"
)[-1],
# Convert decimal to any base
"convert_decimal": lambda refs: lambda base: (
n := refs["value"](),
digits := [],
[digits.append(int(n % base)) or (n := n // base) for _ in range(32) if n > 0],
"".join(str(d) if d < 10 else chr(ord("a") + d - 10) for d in reversed(digits) or [0])
)[-1],
"format_hint": lambda refs: lambda base: " (use lowercase letters a-z for digits above 9)" if base > 10 and refs["hint"]() else ""
},
# Define composition templates
"templates": {
"value": lambda refs: {
"template": "{val}",
"parts": {
"val": lambda refs=refs: refs["convert_decimal"](refs)(refs["src_base"](refs))
}
},
"base_src": lambda refs: (
base := refs["src_base"](refs),
{
"template": "{base}",
"parts": {
"base": lambda refs=refs: refs["format_base"](refs)(base)
}
}
)[-1],
"base_trg": lambda refs: (
base := refs["trg_base"](refs),
{
"template": "{base}{hint}",
"parts": {
"base": lambda refs=refs: refs["format_base"](refs)(base),
"hint": lambda refs=refs: refs["format_hint"](refs)(base)
}
}
)[-1]
}
}