rotate matrix

This commit is contained in:
Zafir Stojanovski 2025-02-08 14:27:10 +01:00
parent def66e0d40
commit fe83bee725
5 changed files with 362 additions and 0 deletions

View file

@ -24,6 +24,7 @@ This gallery shows examples from all available datasets using their default conf
- [gsm_symbolic](#gsm_symbolic)
- [intermediate_integration](#intermediate_integration)
- [isomorphic_strings](#isomorphic_strings)
- [rotate_matrix](#rotate_matrix)
- [largest_island](#largest_island)
- [lcm](#lcm)
- [leg_counting](#leg_counting)
@ -1167,6 +1168,166 @@ Metadata: {'words': ['hogtytyof', 'kgqwfwfgh'], 'solution': True, 'solvable': Tr
````
### rotate_matrix
Rotate a matrix by 90 degrees clockwise
Default configuration:
```python
max_n: int = 10 # Maximum size of the matrix
```
````
Sample 1:
Question: Given a square matrix, your job is to rotate it by 90 degrees clockwise.
Example:
Input:
1 2 3
4 5 6
7 8 9
Output:
7 4 1
8 5 2
9 6 3
Rotate the matrix below by 90 degrees clockwise:
3 1
2 0
Answer:
2 3
0 1
Metadata: {'matrix': [[3, 1], [2, 0]], 'solution': [[2, 3], [0, 1]]}
--------------------------------------------------
Sample 2:
Question: Given a square matrix, your job is to rotate it by 90 degrees clockwise.
Example:
Input:
1 2 3
4 5 6
7 8 9
Output:
7 4 1
8 5 2
9 6 3
Rotate the matrix below by 90 degrees clockwise:
0
Answer:
0
Metadata: {'matrix': [[0]], 'solution': [[0]]}
--------------------------------------------------
Sample 3:
Question: Given a square matrix, your job is to rotate it by 90 degrees clockwise.
Example:
Input:
1 2 3
4 5 6
7 8 9
Output:
7 4 1
8 5 2
9 6 3
Rotate the matrix below by 90 degrees clockwise:
28 17 38 29 8 15 26
35 13 37 39 27 40 20
4 30 23 16 3 5 48
9 25 2 46 47 21 22
31 12 41 43 19 32 10
6 0 36 45 42 1 18
14 24 11 7 44 34 33
Answer:
14 6 31 9 4 35 28
24 0 12 25 30 13 17
11 36 41 2 23 37 38
7 45 43 0 16 39 29
44 42 19 47 3 27 8
34 1 32 21 5 40 15
33 18 10 22 48 20 26
Metadata: {'matrix': [[28, 17, 38, 29, 8, 15, 26], [35, 13, 37, 39, 27, 40, 20], [4, 30, 23, 16, 3, 5, 48], [9, 25, 2, 46, 47, 21, 22], [31, 12, 41, 43, 19, 32, 10], [6, 0, 36, 45, 42, 1, 18], [14, 24, 11, 7, 44, 34, 33]], 'solution': [[14, 6, 31, 9, 4, 35, 28], [24, 0, 12, 25, 30, 13, 17], [11, 36, 41, 2, 23, 37, 38], [7, 45, 43, 0, 16, 39, 29], [44, 42, 19, 47, 3, 27, 8], [34, 1, 32, 21, 5, 40, 15], [33, 18, 10, 22, 48, 20, 26]]}
--------------------------------------------------
Sample 4:
Question: Given a square matrix, your job is to rotate it by 90 degrees clockwise.
Example:
Input:
1 2 3
4 5 6
7 8 9
Output:
7 4 1
8 5 2
9 6 3
Rotate the matrix below by 90 degrees clockwise:
20 3 6 19 22
5 7 17 11 16
24 14 12 4 1
18 23 21 0 10
9 2 8 15 13
Answer:
9 18 24 5 20
2 23 14 7 3
8 21 0 17 6
15 0 4 11 19
13 10 1 16 22
Metadata: {'matrix': [[20, 3, 6, 19, 22], [5, 7, 17, 11, 16], [24, 14, 12, 4, 1], [18, 23, 21, 0, 10], [9, 2, 8, 15, 13]], 'solution': [[9, 18, 24, 5, 20], [2, 23, 14, 7, 3], [8, 21, 0, 17, 6], [15, 0, 4, 11, 19], [13, 10, 1, 16, 22]]}
--------------------------------------------------
Sample 5:
Question: Given a square matrix, your job is to rotate it by 90 degrees clockwise.
Example:
Input:
1 2 3
4 5 6
7 8 9
Output:
7 4 1
8 5 2
9 6 3
Rotate the matrix below by 90 degrees clockwise:
1 2
0 3
Answer:
0 1
3 2
Metadata: {'matrix': [[1, 2], [0, 3]], 'solution': [[0, 1], [3, 2]]}
--------------------------------------------------
````
### largest_island
Generates Largest Island exercises with configurable difficulty

View file

@ -101,6 +101,7 @@ See the [Dataset Gallery](https://github.com/open-thought/reasoning-gym/blob/mai
- `WordLadderDataset`: Generate word ladder puzzles where one word is transformed into another by changing one letter at a time
- `GroupAnagramsDataset`: Group anagrams together in a list of words
- `IsomorphicStrings`: Check if two strings are isomorphic (have the same character mapping)
- `RotateMatrix`: Rotate a matrix by 90 degrees clockwise
### <small>Code Tasks</small>

View file

@ -15,6 +15,7 @@ from .letter_jumble import LetterJumbleConfig, LetterJumbleDataset
from .number_filtering import NumberFilteringConfig, NumberFilteringDataset
from .number_sorting import NumberSortingConfig, NumberSortingDataset
from .palindrome_generation import PalindromeConfig, PalindromeDataset
from .rotate_matrix import RotateMatrixConfig, RotateMatrixDataset
from .sentence_reordering import SentenceReorderingConfig, SentenceReorderingDataset
from .spell_backward import SpellBackwardConfig, SpellBackwardDataset
from .word_ladder import WordLadderConfig, WordLadderDataset
@ -51,4 +52,6 @@ __all__ = [
"GroupAnagramsDataset",
"IsomorphicStringsConfig",
"IsomorphicStringsDataset",
"RotateMatrixConfig",
"RotateMatrixDataset",
]

View file

@ -0,0 +1,97 @@
"""Rotate a square matrix by 90 degrees clockwise.
A popular Leetcode problem:
https://leetcode.com/problems/rotate-image/description/
"""
from copy import deepcopy
from dataclasses import dataclass
from random import Random
from typing import Optional
from ..factory import ProceduralDataset, register_dataset
QUESTION_TEMPLATE = """Given a square matrix, your job is to rotate it by 90 degrees clockwise.
Example:
Input:
1 2 3
4 5 6
7 8 9
Output:
7 4 1
8 5 2
9 6 3
Rotate the matrix below by 90 degrees clockwise:
{matrix}
"""
@dataclass
class RotateMatrixConfig:
"""Configuration for Rotate Matrix dataset generation"""
max_n: int = 10 # Maximum size of the matrix
size: int = 500 # Virtual dataset size
seed: Optional[int] = None
def validate(self):
"""Validate configuration parameters"""
assert 1 <= self.max_n, "max_n must be at least 1"
class RotateMatrixDataset(ProceduralDataset):
"""Generates Rotate Matrix exercises with configurable difficulty"""
def __init__(self, config: RotateMatrixConfig):
super().__init__(config=config, seed=config.seed, size=config.size)
def _get_matrix(self, rng: Random) -> list[list[int]]:
"""Generate a random matrix"""
n = rng.randint(1, self.config.max_n)
numbers = list(range(n**2))
rng.shuffle(numbers)
matrix = [numbers[i * n : (i + 1) * n] for i in range(n)]
return matrix
def _get_rotated(self, matrix: list[list[int]]) -> list[list[int]]:
"""Rotate the matrix by 90 degrees clockwise"""
n = len(matrix)
output = deepcopy(matrix)
for l in range(n // 2):
for i in range(l, n - 1 - l):
(output[l][i], output[i][n - 1 - l], output[n - 1 - l][n - 1 - i], output[n - 1 - i][l]) = (
matrix[n - 1 - i][l],
matrix[l][i],
matrix[i][n - 1 - l],
matrix[n - 1 - l][n - 1 - i],
)
return output
def _matrix_to_str(self, matrix: list[list[int]]) -> str:
"""Get a string representation of the matrix"""
return "\n".join(" ".join(str(x) for x in row) for row in matrix)
def __getitem__(self, idx: int) -> dict:
"""Generate a single Spiral Matrix question"""
rng = Random(self.seed + idx)
matrix = self._get_matrix(rng)
matrix_str = self._matrix_to_str(matrix)
answer = self._get_rotated(matrix)
answer_str = self._matrix_to_str(answer)
return {
"question": QUESTION_TEMPLATE.format(matrix=matrix_str),
"answer": answer_str,
"metadata": {"matrix": matrix, "solution": answer},
}
register_dataset("rotate_matrix", RotateMatrixDataset, RotateMatrixConfig)

100
tests/test_rotate_matrix.py Normal file
View file

@ -0,0 +1,100 @@
"""Tests for Rotate Matrix questions generation"""
import pytest
from reasoning_gym.algorithmic.rotate_matrix import RotateMatrixConfig, RotateMatrixDataset
def test_rotate_matrix_config_validation():
"""Test that invalid configs raise appropriate errors"""
with pytest.raises(AssertionError):
config = RotateMatrixConfig(max_n=-1) # Negative not allowed
config.validate()
with pytest.raises(AssertionError):
config = RotateMatrixConfig(max_n=0) # Zero not allowed
config.validate()
def test_rotate_matrix_dataset_deterministic():
"""Test that dataset generates same items with same seed"""
config = RotateMatrixConfig(seed=42, size=10)
dataset1 = RotateMatrixDataset(config)
dataset2 = RotateMatrixDataset(config)
for i in range(len(dataset1)):
assert dataset1[i] == dataset2[i]
def test_rotate_matrix_dataset_items():
"""Test basic properties of generated items"""
config = RotateMatrixConfig(max_n=7, size=10, seed=42)
dataset = RotateMatrixDataset(config)
for i in range(len(dataset)):
item = dataset[i]
# Check item structure
assert isinstance(item, dict)
assert "question" in item
assert "answer" in item
assert "metadata" in item
# Check metadata
assert "matrix" in item["metadata"]
assert "solution" in item["metadata"]
matrix = item["metadata"]["matrix"]
solution = item["metadata"]["solution"]
# Verify matrix dimensions
assert len(matrix) <= config.max_n
assert all(len(row) <= config.max_n for row in matrix)
assert len(solution) <= config.max_n
assert all(len(row) <= config.max_n for row in solution)
assert set(e for row in matrix for e in row) == set(e for row in solution for e in row)
def test_rotate_matrix_dataset_iteration():
"""Test that iteration respects dataset size"""
config = RotateMatrixConfig(size=5, seed=42)
dataset = RotateMatrixDataset(config)
items = list(dataset)
assert len(items) == config.size
# Test multiple iterations yield same items
assert items == list(dataset)
def test_rotate_matrix_answer():
"""Test the _get_rotated method"""
config = RotateMatrixConfig(seed=42)
dataset = RotateMatrixDataset(config)
# n = 1
matrix = [[8]]
expected = [[8]]
assert dataset._get_rotated(matrix) == expected
# n = 2
matrix = [
[0, 1],
[2, 3],
]
expected = [
[2, 0],
[3, 1],
]
assert dataset._get_rotated(matrix) == expected
# n = 3
matrix = [
[0, 1, 2],
[3, 4, 5],
[6, 7, 8],
]
expected = [
[6, 3, 0],
[7, 4, 1],
[8, 5, 2],
]