import random import unittest from unittest.mock import MagicMock, patch from bs4 import BeautifulSoup from olmocr.bench.tests import TestType from olmocr.synth.mine_html_templates import ( PreserveTablesConverter, extract_html_metadata, generate_tests_from_html, html_to_markdown_with_frontmatter, ) class TestMathExtraction(unittest.TestCase): """Test the math extraction functionality in mine_html_templates.py""" def setUp(self): self.random_generator = random.Random(42) return super().setUp() def test_math_extraction_from_html(self): """Test that math equations are properly extracted from HTML content""" html_content = """
Some text with inline math \\(x = 2\\) here.
Display math: \\[E = mc^2\\]
Another inline: \\(\\alpha + \\beta = \\gamma\\)
Complex display: \\[\\int_0^\\infty e^{-x} dx = 1\\]
""" # Generate tests from HTML tests = generate_tests_from_html(html_content, "test_pdf", 1, self.random_generator) # Filter math tests math_tests = [t for t in tests if t.get("type") == "math"] # Check that we extracted math equations self.assertTrue(len(math_tests) > 0, "Should extract at least one math equation") # Check that specific equations were extracted math_contents = [t["math"] for t in math_tests] self.assertIn("x = 2", math_contents) self.assertIn("E = mc^2", math_contents) self.assertIn("\\alpha + \\beta = \\gamma", math_contents) self.assertIn("\\int_0^\\infty e^{-x} dx = 1", math_contents) def test_math_extraction_with_multiline(self): """Test extraction of multiline math equations""" html_content = """Multiline equation: \\[ e_i = \\frac{e_i + \\varphi(e_i)}{2} + \\frac{e_i - \\varphi(e_i)}{2}, \\quad \\text{for } i \\in \\mathbb{N}. \\]
""" tests = generate_tests_from_html(html_content, "test_pdf", 1, self.random_generator) math_tests = [t for t in tests if t.get("type") == "math"] # Check multiline equation is captured self.assertTrue(len(math_tests) > 0) # Check that the multiline content is preserved (without excessive newlines) found_multiline = False for test in math_tests: if "\\frac{e_i + \\varphi(e_i)}{2}" in test["math"] and "\\mathbb{N}" in test["math"]: found_multiline = True break self.assertTrue(found_multiline, "Should extract multiline equation correctly") def test_math_extraction_deduplication(self): """Test that duplicate math equations are deduplicated""" html_content = """First occurrence: \\[x^2 + y^2 = z^2\\]
Second occurrence: \\[x^2 + y^2 = z^2\\]
Third occurrence: \\[x^2 + y^2 = z^2\\]
""" tests = generate_tests_from_html(html_content, "test_pdf", 1, self.random_generator) math_tests = [t for t in tests if t.get("type") == "math"] # Count how many times the equation appears equation_count = sum(1 for t in math_tests if "x^2 + y^2 = z^2" in t["math"]) # Should only appear once due to deduplication self.assertEqual(equation_count, 1, "Duplicate equations should be deduplicated") def test_math_extraction_patterns(self): """Test different math delimiter patterns""" html_content = """Pattern 1: \\(inline1\\)
Pattern 2: \\[display1\\]
Pattern 3: $$display2$$
""" tests = generate_tests_from_html(html_content, "test_pdf", 1, self.random_generator) math_tests = [t for t in tests if t.get("type") == "math"] math_contents = [t["math"] for t in math_tests] # Check all patterns are captured self.assertIn("inline1", math_contents) self.assertIn("display1", math_contents) self.assertIn("display2", math_contents) def test_math_extraction_minimum_length(self): """Test that very short equations are filtered out""" html_content = """Short: \\(x\\)
Also short: \\[y\\]
Long enough: \\(x=1\\)
""" tests = generate_tests_from_html(html_content, "test_pdf", 1, self.random_generator) math_tests = [t for t in tests if t.get("type") == "math"] math_contents = [t["math"] for t in math_tests] # Short equations (length <= 2) should be filtered out self.assertNotIn("x", math_contents) self.assertNotIn("y", math_contents) # Longer equation should be included self.assertIn("x=1", math_contents) def test_math_validation_passes(self): """Test that valid math tests pass validation against markdown""" html_content = """Test equation: \\[E = mc^2\\]
""" # Mock the validation to always pass for math tests with patch("olmocr.bench.tests.load_single_test") as mock_load: mock_test = MagicMock() mock_test.run.return_value = (True, None) mock_load.return_value = mock_test tests = generate_tests_from_html(html_content, "test_pdf", 1, self.random_generator) math_tests = [t for t in tests if t.get("type") == "math"] # Verify math test was created self.assertTrue(len(math_tests) > 0) # Verify test has correct structure for test in math_tests: self.assertEqual(test["type"], "math") self.assertIn("math", test) self.assertEqual(test["max_diffs"], 0) self.assertIn("id", test) self.assertIn("pdf", test) self.assertEqual(test["page"], 1) def test_complex_markdown_example(self): """Test with the complex markdown example provided by the user""" # Convert markdown to HTML-like structure for testing html_content = '\n\n\n \n \nIf \\(\\varphi \\in \\text{Aut}(E)\\) with \\(\\varphi^2 = id\\) we observe that
\n \\[e_i = \\frac{e_i + \\varphi(e_i)}{2} + \\frac{e_i - \\varphi(e_i)}{2}, \\quad \\text{for } i \\in \\mathbb{N}.\\]\n \nSetting \\(a_i = e_i + \\varphi(e_i)/2\\) we have:
\n \nLet \\(\\varphi \\in \\text{Aut}(E)\\). We say that \\(\\varphi\\) is of canonical type if \\(\\varphi(e_i) \\in E_{(1)}\\) for all \\(i\\).
\n \nIf \\(\\varphi\\) is an automorphism of order 2 on \\(E\\), we have that \\(\\varphi\\) is of canonical type if and only if \\(a_i \\in E_{(1)}\\) for all \\(i\\). Let us fix a basis \\(\\beta = \\{e_1, e_2, \\ldots, e_n, \\ldots\\}\\) of the vector space \\(L\\) and an automorphism \\(\\varphi \\in \\text{Aut}(E)\\) such that \\(\\varphi^2 = id\\). Then \\(\\varphi\\), as a linear transformation, has eigenvalues \\(\\pm 1\\) and \\(-1\\) only, and moreover, there exists a basis of the vector space \\(E\\) consisting of eigenvectors. (It is well known from elementary Linear Algebra that this fact does not depend on the dimension of the vector space as long as the characteristic of \\(F\\) is different from 2.) Then \\(E = E(1) \\oplus E(-1)\\) where \\(E(t)\\) is the eigenspace for the eigenvalue \\(t\\) of the linear transformation \\(\\varphi\\). One considers the intersections \\(L(t) = L \\cap E(t)\\), \\(t = \\pm 1\\). Changing the basis \\(\\beta\\), if necessary, one may assume that \\(L(t)\\) is the span of \\(\\beta \\cap L(t)\\). Clearly this change of basis gives rise to a homogeneous automorphism of \\(E\\) and we can take the composition of it and then \\(\\varphi\\). We shall assume that such a change of basis has been done.
\n \nDenote
\n \\[I_\\varphi = \\{n \\in \\mathbb{N} \\mid \\varphi(e_n) = \\pm e_n\\}.\\]\nWe shall distinguish the following four possibilities:
\n \nWe shall call these automorphisms (and also the corresponding \\(\\mathbb{Z}_2\\)-gradings), automorphisms (or gradings) of type 1, 2, 3, and 4, respectively.
\n \nThe automorphisms of type 1 induce \\(\\mathbb{Z}_2\\)-gradings on \\(E\\) in which all generators of \\(E\\) are homogeneous. Such structures are called homogeneous \\(\\mathbb{Z}_2\\)-gradings on \\(E\\). The corresponding graded identities were completely studied in [22, 24, 29].
\n \nWe conclude this section with the following lemma.
\n \nLet \\(\\varphi\\) be an automorphism of order two of \\(E\\). Then \\(\\varphi\\) is of type 4 if and only if, for every \\(v \\in L\\) such that \\(\\varphi(v) = \\pm v\\), one has \\(v = 0\\).
\n \nFrom this point on, our goal is to survey recent developments regarding automorphisms of order two and the corresponding \\(\\mathbb{Z}_2\\)-gradings of the infinite-dimensional Grassmann algebra.
\n \nLet \\(X = \\{e_1, \\ldots, e_n, \\ldots\\}\\). For each map \\(\\lambda : X \\to E\\), we can define the linear transformation \\(\\varphi : E \\to E\\) by
\n \nfor all \\(n \\in \\mathbb{N}\\).
\n \nWe start with the next lemma.
\n \nThe linear transformation \\(\\varphi\\) is an endomorphism of \\(E\\) if and only if
\n \\[\\lambda(e_i)\\lambda(e_j) + \\lambda(e_j)\\lambda(e_i) = 0, \\quad \\text{for all } i, j.\\]\n\\[ x = y + z \\]
""" tests = generate_tests_from_html(html_content, "test_pdf", 1, self.random_generator) math_tests = [t for t in tests if t.get("type") == "math"] self.assertTrue(len(math_tests) > 0) # The equation should be stripped of leading/trailing whitespace self.assertEqual(math_tests[0]["math"].strip(), math_tests[0]["math"]) def test_math_not_in_present_tests(self): """Test that extracted math equations have whitespace properly stripped""" html_content = """\\[x = y + z \\mathcal{x} \\]
\\[y = x + z \\mathcal{x} \\]
\\[q = r + z \\mathcal{x} \\]
""" tests = generate_tests_from_html(html_content, "test_pdf", 1, self.random_generator) present_tests = [t for t in tests if t.get("type") == "present"] self.assertTrue(len(present_tests) == 0) if __name__ == "__main__": unittest.main()