CoolFace
Datasetpublic

beichen0426/olympiads-ref

AI-MO Olympiad Reference Dataset This dataset contains a structured collection of Olympiad problems and their solutions, organized by competition. Contains high quality data, prioritizing "official" solutions to problems. Structure <competition name>/ # Problems and solutions from the International Mathematical Olympiad ├── raw/ # Raw problem/solution statements (.pdf) │ ├── file1.pdf │ ├── file2.pdf ├── download_script/ # the scripts used… See the full description on the dataset page: https://huggingface.co/datasets/beichen0426/olympiads-ref.

sourceHugging Faceupdated 9mo agoView on Hugging Face
0likes8.9kdownloads
segment.py186 linesDownload Raw Back to segment_script
1import re2import json3 4from tqdm import tqdm5from loguru import logger6 7from pathlib import Path8from typing import Tuple, List9from dataclasses import dataclass10 11 12project_root = Path(__file__).parent.parent.parent13 14 15@dataclass16class Problem:17    match: re.Match18 19 20@dataclass21class Solution:22    match: re.Match23 24 25def clean_text(text: str) -> str:26    text = text.replace(27        'For a discussion, see\nW. Morris and V. Soltan. The Erdős-Szekeres Problem on Points in Convex Postion-A Survey, Bulletin of the American Math Monthly. 37 (2000), 437-458.\n\nThis article is available at\nhttp://www.ams.org/bull/2000-37-04/S0273-0979-00-00877-6/home.html.\nIf $N(7)=33$, the highest sure score on this problem would be $32-6=26$. It is not known whether there exist arbitrarily large sets of points that will fool the graders.\n\n## The unexamined life is not worth living.',28        ''29    )30    text = text.replace(31        '- Bishop: This piece can move any number of squares diagonally if there are no other pieces along its path.\n- Rook: This piece can move any number of squares either vertically or horizontally if there are no other pieces along its path\n- Knight: This piece can move either two squares along a row and one square along a column or two squares along a column and one square along a row.\n- King: This piece can move to any open adjacent square (including diagonally).',32        ''33    )34    return text35 36 37def find_problem_with_solution(38    text: str,39    problem_parttern: re.Pattern,40    solution_pattern: re.Pattern41) -> int:42    """43    Find the problem with solution start position in the text.44    Args:45        text (str): The text to search.46    Returns:47        int: The start position of the problem with solution.48    """49    matchs = list(problem_parttern.finditer(text))50 51    for index, match in enumerate(matchs):52        section_end_position = matchs[index + 1].start() if index + 1 < len(matchs) else len(text)53        if solution_pattern.search(text[match.start():section_end_position]):54            return match.start()55 56    return 057 58 59def analyze(text: str) -> Tuple[List[Problem | Solution], int]:60    """61    Analyze the text and return the tags and problem number.62    Args:63        text (str): The markdown text to analyze.64    Returns:65        Tuple[List[Problem | Solution], int]: A tuple containing the tags and problem number.66    """67    problem_pattern = re.compile(r'(?:\n|\n\#+\s+)(?:(\d{1,2})\.\s+(?:problem\:\s*|\$?\[.+?\]\$?)?|problem\s+?(\w+)\s+\[\d+(?:\spoints)?\]|\$([H|M|T]_\{\d+\})\$\.)', re.IGNORECASE)68    solution_pattern = re.compile(r'(?:\n|\n\#+\s+)(?:answer\:|solution(?:\s+\d+)?(?:\:|\.)|Proposed by:.*?\n)\s*', re.IGNORECASE)69 70    start_position = find_problem_with_solution(text, problem_pattern, solution_pattern)71 72    tags: List[Problem | Solution] = []73    tags.extend([Problem(x) for x in problem_pattern.finditer(text, start_position)])74    problem_num = len(tags)75 76    tags.extend([Solution(x) for x in solution_pattern.finditer(text, start_position)])77    tags.sort(key=lambda x: x.match.start())78    return tags, problem_num79 80 81def segment(text: str, tags: List[Problem | Solution]) -> List[str]:82    starts = []83    ends = []84 85    for i in range(len(tags)):86        starts.append(tags[i].match.end())87        if i + 1 < len(tags):88            ends.append(tags[i + 1].match.start())89        else:90            ends.append(len(text))91 92    return [text[start:end].strip() for start, end in zip(starts, ends)]93 94 95def join(tags: List[Problem | Solution], segments: List[str]) -> List[Tuple[str, str, str, str, str]]:96    problem, solution = '', ''97    problem_label, problem_match, solution_match = '', '', ''98    pairs = []99 100    for tag, segment in zip(tags, segments):101        if isinstance(tag, Problem):102            problem = segment103            problem_match = tag.match.group(0)104            problem_label = tag.match.group(1) or tag.match.group(2) or tag.match.group(3)105        elif problem.strip() != "":106            solution = segment107            solution_match = tag.match.group(0)108 109            if solution.strip() == "":110                continue111 112            pairs.append((problem, solution, problem_label, problem_match, solution_match))113 114    return pairs115 116 117def write_pairs(output_file: Path, pairs):118    year = re.search(r'(\d{4})', output_file.stem).group(1)119    problem_type_mapping = {120        "-alg-": "Algebra",121        "-comb-": "Combinatorics",122        "-geo-": "Geometry",123    }124 125    problem_type = None126    for _k, _v in problem_type_mapping.items():127        if _k in output_file.stem:128            problem_type = _v129            break130 131    output_jsonl_text = ""132    for problem, solution, problem_label, problem_match, solution_match in pairs:133        output_jsonl_text += json.dumps(134            {135                'year': year,136                'tier': "T4",137                'problem_label': problem_label,138                'problem_type': problem_type,139                "exam": "HMMT", 140                'problem': problem,141                'solution': solution,142                'metadata': {143                    'resource_path': output_file.relative_to(project_root).as_posix(),144                    'problem_match': problem_match,145                    'solution_match': solution_match146                }147            },148            ensure_ascii=False149        ) + '\n'150 151    output_file.write_text(output_jsonl_text, encoding="utf-8")152 153 154def main():155    compet_base_path = Path(__file__).resolve().parent.parent156    compet_md_path = compet_base_path / "md"157    seg_output_path = compet_base_path / "segmented"158 159    total_problem_count = 0160    total_solution_count = 0161 162    for hmmt_md in tqdm(list(compet_md_path.glob('**/*.md')), desc='Segmenting'):163        output_file = seg_output_path / hmmt_md.relative_to(compet_md_path).with_suffix('.jsonl')164        output_file.parent.mkdir(parents=True, exist_ok=True)165 166        text = '\n' + clean_text(hmmt_md.read_text(encoding="utf-8"))167 168        tags, problem_num = analyze(text)169 170        segments = segment(text, tags)171        pairs = join(tags, segments)     172        if pairs and problem_num > 0:173            write_pairs(output_file, pairs)174 175            total_problem_count += problem_num176            total_solution_count += len(pairs)177        else:178            logger.warning(f"No problem found in {hmmt_md}")179        180    logger.info(f"Total problem count: {total_problem_count}")181    logger.info(f"Total solution count: {total_solution_count}")182 183 184if __name__ == '__main__':185    main()186 
beichen0426/olympiads-ref · CoolFace