From ecac3847e4a1b5f2d68317c45b53c47f8f7e8450 Mon Sep 17 00:00:00 2001 From: Jake Poznanski Date: Tue, 4 Mar 2025 08:27:47 -0800 Subject: [PATCH 1/8] Making a nicer warning message when waiting for sglang server --- olmocr/pipeline.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/olmocr/pipeline.py b/olmocr/pipeline.py index e82b42d..481275f 100644 --- a/olmocr/pipeline.py +++ b/olmocr/pipeline.py @@ -643,7 +643,7 @@ async def sglang_server_ready(): else: logger.info(f"Attempt {attempt}: Unexpected status code {response.status_code}") except Exception as e: - logger.warning(f"Attempt {attempt}: {e}") + logger.warning(f"Attempt {attempt}: Please wait for sglang server to become ready...") await asyncio.sleep(delay_sec) From fcb1eab98f331a1bb674810e16cf1f11fab45ed6 Mon Sep 17 00:00:00 2001 From: Jake Poznanski Date: Tue, 4 Mar 2025 08:39:35 -0800 Subject: [PATCH 2/8] Consistent ordering on convert, with data dir script --- olmocr/bench/convert.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/olmocr/bench/convert.py b/olmocr/bench/convert.py index 72d4092..5a94f9d 100644 --- a/olmocr/bench/convert.py +++ b/olmocr/bench/convert.py @@ -52,7 +52,10 @@ async def process_pdfs(config, pdf_directory, data_directory, repeats): kwargs = config[candidate]["kwargs"] is_async = asyncio.iscoroutinefunction(method) - for pdf_path in tqdm(glob.glob(os.path.join(pdf_directory, "*.pdf")), desc=candidate): + all_pdfs = glob.glob(os.path.join(pdf_directory, "*.pdf")) + all_pdfs.sort() + + for pdf_path in tqdm(all_pdfs, desc=candidate): base_name = os.path.basename(pdf_path).replace(".pdf", "") for i in range(1, repeats + 1): @@ -86,6 +89,7 @@ if __name__ == "__main__": "Use 'name=folder_name' to specify a custom output folder name.", ) parser.add_argument("--repeats", type=int, default=1, help="Number of times to repeat the conversion for each PDF.") + parser.add_argument("--dir", type=str, default=os.path.join(os.path.dirname(__file__), "mining_data"), help="Path to the data folder in which to save outputs, pdfs should be in /pdfs folder within it.") args = parser.parse_args() # Mapping of method names to a tuple: (module path, function name) @@ -109,7 +113,7 @@ if __name__ == "__main__": function = getattr(module, function_name) config[method_name] = {"method": function, "kwargs": extra_kwargs, "folder_name": folder_name} - data_directory = os.path.join(os.path.dirname(__file__), "mining_data") + data_directory = args.dir pdf_directory = os.path.join(data_directory, "pdfs") # Run the async process function From c4f6b118343ef782d29c5ed365c0af5e7bc51f45 Mon Sep 17 00:00:00 2001 From: Jake Poznanski Date: Tue, 4 Mar 2025 09:11:53 -0800 Subject: [PATCH 3/8] Fixing the mine diffs script, but it still doesn't work great --- olmocr/bench/miners/mine_diffs.py | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/olmocr/bench/miners/mine_diffs.py b/olmocr/bench/miners/mine_diffs.py index aaea6db..e096867 100644 --- a/olmocr/bench/miners/mine_diffs.py +++ b/olmocr/bench/miners/mine_diffs.py @@ -119,7 +119,7 @@ def compare_votes_for_file(base_pdf_file: str, base_pdf_page: int, base_text: st best_candidate = c_sentence # Keep original capitalization for output # Append the candidate if it passes the similarity threshold (e.g., 0.7) - if best_ratio > 0.7 and best_candidate is not None: + if best_ratio > 0.5 and best_candidate is not None: votes.append(best_candidate.strip()) # Only consider variants that differ when compared case-insensitively @@ -191,13 +191,6 @@ def main(): # Collect all .md files from the base and compare folders base_files = [f for f in os.listdir(base_path) if f.endswith(".md")] - compare_files = [f for f in os.listdir(compare_path) if f.endswith(".md")] - - # Read all candidate texts at once - candidate_texts = [] - for cf in compare_files: - with open(os.path.join(compare_path, cf), "r", encoding="utf-8") as f: - candidate_texts.append(f.read()) all_tests = [] @@ -207,6 +200,17 @@ def main(): with open(base_file_path, "r", encoding="utf-8") as f: base_text = f.read() + compare_files = [f for f in os.listdir(compare_path) if f.endswith(".md") and re.sub(r"_\d+\.md$", "", f) == re.sub(r"_\d+\.md$", "", bf)] + + if not compare_files: + print(f"skipping {bf} nothing to compare against") + + # Read all candidate texts at once + candidate_texts = [] + for cf in compare_files: + with open(os.path.join(compare_path, cf), "r", encoding="utf-8") as f: + candidate_texts.append(f.read()) + base_pdf_file = get_pdf_from_md(base_file_path) base_pdf_page = 1 print(f"Results for base file: {bf}") From 76476f99920a64cb2a2bd8e2e3c87b5b8ef37212 Mon Sep 17 00:00:00 2001 From: Jake Poznanski Date: Tue, 4 Mar 2025 09:59:51 -0800 Subject: [PATCH 4/8] Synth rendering ideas --- olmocr/bench/synth/__init__.py | 0 olmocr/bench/synth/render.py | 182 +++++++++++++++++++ olmocr/bench/synth/templates/bookpage.js | 214 +++++++++++++++++++++++ 3 files changed, 396 insertions(+) create mode 100644 olmocr/bench/synth/__init__.py create mode 100644 olmocr/bench/synth/render.py create mode 100644 olmocr/bench/synth/templates/bookpage.js diff --git a/olmocr/bench/synth/__init__.py b/olmocr/bench/synth/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/olmocr/bench/synth/render.py b/olmocr/bench/synth/render.py new file mode 100644 index 0000000..c8d1647 --- /dev/null +++ b/olmocr/bench/synth/render.py @@ -0,0 +1,182 @@ +#!/usr/bin/env python3 +import os +import asyncio +from pathlib import Path +from playwright.async_api import async_playwright + +# Simple configuration +CONFIG = { + "input_file": os.path.join(os.path.dirname(__file__), "templates", "bookpage.js"), # React component file + "output_pdf": "book-page.pdf", # Output PDF filename + "temp_html": "temp-render.html", # Temporary HTML file + "wait_time": 1500, # Time to wait for rendering (ms) + "device_scale": 2, # Resolution multiplier + "debug": True # Keep temp files for debugging +} + +async def create_html_file(): + """Create a temporary HTML file that loads the React component from a file.""" + try: + # Check if input file exists + input_path = Path(CONFIG["input_file"]) + if not input_path.exists(): + print(f"Error: Input file '{input_path}' not found") + return False + + # Read the component file + with open(input_path, 'r', encoding='utf-8') as f: + component_code = f.read() + + # Create HTML that will load our component + html_content = """ + + + + + + Book Page Template + + + + + + +
+ + + + + """ + + with open(CONFIG["temp_html"], 'w', encoding='utf-8') as f: + f.write(html_content) + + print(f"Created HTML file: {CONFIG['temp_html']}") + print(f"Using React component from: {CONFIG['input_file']}") + return True + except Exception as e: + print(f"Error creating HTML file: {e}") + print(f"Exception details: {str(e)}") + import traceback + traceback.print_exc() + return False + +async def render_to_pdf(): + """Render the React component to PDF using Playwright.""" + try: + # Create the HTML file first + html_created = await create_html_file() + if not html_created: + print("Failed to create HTML file") + return + + print("Launching browser...") + async with async_playwright() as p: + # Launch the browser with more debugging options + browser = await p.chromium.launch( + headless=True, # True for production, False for debugging + ) + + # Create a new page for letter size paper + page = await browser.new_page( + viewport={"width": 816, "height": 1056}, # 8.5in x 11in at 96dpi + device_scale_factor=CONFIG["device_scale"] + ) + + # Get absolute path to HTML file + html_path = Path(CONFIG["temp_html"]).absolute() + html_uri = f"file://{html_path}" + + print(f"Navigating to: {html_uri}") + + # Add event listeners for console messages and errors + page.on("console", lambda msg: print(f"Browser console: {msg.text}")) + page.on("pageerror", lambda err: print(f"Browser page error: {err}")) + + # Navigate with longer timeout and wait for network idle + await page.goto(html_uri, wait_until="networkidle", timeout=30000) + + # Wait for React to render + await page.wait_for_timeout(CONFIG["wait_time"]) + + # Add a check to ensure the component rendered + element_count = await page.evaluate("""() => { + const root = document.getElementById('root'); + return root.childElementCount; + }""") + + if element_count == 0: + print("Warning: No elements found in root. Component may not have rendered.") + else: + print(f"Found {element_count} elements in root. Component rendered successfully.") + + # Save debug screenshot + if CONFIG["debug"]: + await page.screenshot(path="debug-screenshot.png") + print("Debug screenshot saved") + + # Generate PDF + print("Generating PDF...") + await page.pdf( + path=CONFIG["output_pdf"], + format="Letter", + print_background=True, + margin={"top": "0", "right": "0", "bottom": "0", "left": "0"} + ) + + print(f"PDF generated successfully: {CONFIG['output_pdf']}") + + # Close the browser + await browser.close() + + # Cleanup temp files if not in debug mode + if not CONFIG["debug"] and Path(CONFIG["temp_html"]).exists(): + Path(CONFIG["temp_html"]).unlink() + print("Temporary HTML file removed") + + except Exception as e: + print(f"Error generating PDF: {e}") + +if __name__ == "__main__": + # Run the async function + try: + asyncio.run(render_to_pdf()) + except Exception as e: + print(f"Fatal error: {e}") + import traceback + traceback.print_exc() \ No newline at end of file diff --git a/olmocr/bench/synth/templates/bookpage.js b/olmocr/bench/synth/templates/bookpage.js new file mode 100644 index 0000000..e855004 --- /dev/null +++ b/olmocr/bench/synth/templates/bookpage.js @@ -0,0 +1,214 @@ +//import React from 'react'; + +const BookPageTemplate = () => { + // Only three state variables as requested + const [title, setTitle] = React.useState("ADVENTURES OF DON QUIXOTE"); + const [pageNumber, setPageNumber] = React.useState("289"); + const [text, setText] = React.useState( + "deed,\" said Don Quixote, \"thou hast hit the point, Sancho, which can alone shake my resolution; I neither can, nor ought to, draw my sword, as I have often told thee, against those who are not dubbed knights. To thee which I had premeditated, thy share of the booty would have been at least the emperor's crown of gold and Cupid's painted wings; for I would have plucked them off perforce, and delivered them into thy hands.\" \"The" + ); + + // Styles for heavily degraded scan effect + const heavilyDegradedStyles = { + filter: 'grayscale(30%) contrast(120%) brightness(85%) sepia(20%)', + position: 'relative', + backgroundColor: '#e6ddc6', // More yellowed aged paper + backgroundImage: 'url("data:image/svg+xml,%3Csvg viewBox=\'0 0 200 200\' xmlns=\'http://www.w3.org/2000/svg\'%3E%3Cfilter id=\'noiseFilter\'%3E%3CfeTurbulence type=\'fractalNoise\' baseFrequency=\'0.85\' numOctaves=\'3\' stitchTiles=\'stitch\'/%3E%3C/filter%3E%3Crect width=\'100%25\' height=\'100%25\' filter=\'url(%23noiseFilter)\' opacity=\'0.25\'/%3E%3C/svg%3E")', + boxShadow: 'inset 0 0 70px rgba(0, 0, 0, 0.3), 0 0 5px rgba(0,0,0,0.1)', + padding: '32px', + borderRadius: '2px', + overflow: 'hidden', + transform: 'rotate(0.3deg)', // Slightly askew scan + }; + + // Heavily degraded text + const badScanTextStyle = { + fontFamily: '"Times New Roman", serif', + letterSpacing: '-0.01em', + wordSpacing: '0.02em', + fontWeight: '500', + color: '#222222', + textShadow: '0 0 1px rgba(0, 0, 0, 0.5)', + transform: 'scale(1.01, 0.99) rotate(-0.4deg)', // Distorted proportions + }; + + // Random coffee stain effect + const coffeeStain = { + position: 'absolute', + width: '100px', + height: '80px', + top: '25%', + right: '15%', + borderRadius: '50%', + background: 'radial-gradient(ellipse at center, rgba(139,69,19,0.15) 0%, rgba(139,69,19,0.1) 50%, rgba(139,69,19,0.05) 70%, rgba(139,69,19,0) 100%)', + transform: 'rotate(30deg) scale(1.5, 1)', + pointerEvents: 'none', + zIndex: 1, + }; + + // Water damage effect + const waterDamage = { + position: 'absolute', + width: '70%', + height: '40%', + bottom: '10%', + left: '5%', + opacity: 0.07, + background: 'radial-gradient(ellipse at center, rgba(0,0,0,0.2) 0%, rgba(0,0,0,0.1) 40%, rgba(0,0,0,0) 70%)', + borderRadius: '40% 60% 70% 30% / 40% 50% 60% 50%', + pointerEvents: 'none', + zIndex: 1, + }; + + // Add fold lines + const foldLine = { + position: 'absolute', + width: '100%', + height: '3px', + top: '30%', + left: 0, + background: 'linear-gradient(to right, rgba(0,0,0,0) 0%, rgba(0,0,0,0.03) 20%, rgba(0,0,0,0.08) 50%, rgba(0,0,0,0.03) 80%, rgba(0,0,0,0) 100%)', + boxShadow: '0 1px 3px rgba(255,255,255,0.2)', + pointerEvents: 'none', + zIndex: 2, + }; + + // Torn edge effect + const tornEdge = { + position: 'absolute', + top: 0, + right: 0, + width: '100%', + height: '100%', + background: 'linear-gradient(135deg, transparent 97%, #e6ddc6 97%, #e6ddc6 100%)', + pointerEvents: 'none', + }; + + return ( +
+ {/* Heavily degraded scan container */} +
+ {/* Noise overlay */} +
+ + {/* Scan lines effect */} +
+ + {/* Add coffee stain */} +
+ + {/* Add water damage */} +
+ + {/* Add fold line */} +
+ + {/* Add torn edge */} +
+ + {/* Header with skewed alignment */} +
+
+

{title}

+
{pageNumber}
+
+ + {/* Horizontal divider with uneven quality */} +
+ + {/* Text content with severely degraded appearance */} +
+ {/* Bad scan text with random character fading */} +

{text.split('').map((char, index) => { + const opacity = Math.random() > 0.8 ? 0.4 + Math.random() * 0.5 : 0.9 + Math.random() * 0.1; + const blur = Math.random() > 0.95 ? 1 : 0; + return {char}; + })}

+
+ + {/* Extra random ink spill */} +
+
+ +
+ ); +}; + +//export default BookPageTemplate; +window.BookPageTemplate = BookPageTemplate; \ No newline at end of file From 748fd62e8a7d0e294ee04f9353b3a3c7e01704d6 Mon Sep 17 00:00:00 2001 From: Jake Poznanski Date: Tue, 4 Mar 2025 13:34:33 -0800 Subject: [PATCH 5/8] Adding basic table relative tests --- olmocr/bench/benchmark.py | 2 +- olmocr/bench/sample_data/dataset.jsonl | 51 ++++--- olmocr/bench/synth/render.py | 2 +- olmocr/bench/synth/templates/listpage.js | 83 ++++++++++ olmocr/bench/tests.py | 186 +++++++++++++++++++++-- 5 files changed, 290 insertions(+), 34 deletions(-) create mode 100644 olmocr/bench/synth/templates/listpage.js diff --git a/olmocr/bench/benchmark.py b/olmocr/bench/benchmark.py index ed3408c..d8b8fa5 100644 --- a/olmocr/bench/benchmark.py +++ b/olmocr/bench/benchmark.py @@ -96,7 +96,7 @@ def evaluate_candidate( if test_avg < 1.0: test_failures.append( f"Test {test.id} on {md_base} average pass ratio: {test_avg:.3f} ({repeat_passes}/{num_repeats} repeats passed). " - f"Example explanation: {explanations[0] if explanations else 'No explanation'}" + f"Ex: {explanations[0] if explanations else 'No explanation'}" ) test_type_breakdown[test_type].append(test_avg) diff --git a/olmocr/bench/sample_data/dataset.jsonl b/olmocr/bench/sample_data/dataset.jsonl index 608ec79..820262e 100644 --- a/olmocr/bench/sample_data/dataset.jsonl +++ b/olmocr/bench/sample_data/dataset.jsonl @@ -1,29 +1,40 @@ -{"pdf": "multi_column_miss.pdf", "page": 1, "id": "multi_column_miss_00", "type": "present", "text": "Corporate social responsibility and the tobacco industry: hope or hype?", "threshold": 0.99} -{"pdf": "multi_column_miss.pdf", "page": 1, "id": "multi_column_miss_01", "type": "present", "text": "this leaves BAT to argue why it should not be held to be largely accountable for the annual deaths of some 754 600 smokers, and Philip Morris some 803 600 smokers.", "threshold": 0.95} -{"pdf": "multi_column_miss.pdf", "page": 1, "id": "multi_column_miss_02", "type": "present", "text": "The term \"corporate social responsibility\" is in vogue at the moment but as a concept it is vague and means different things to different people.", "threshold": 0.95} -{"pdf": "multi_column_miss.pdf", "page": 1, "id": "multi_column_miss_03", "type": "present", "text": "Over the past three decades increasing pressure from non-governmental", "threshold": 1.0} -{"pdf": "multi_column_miss.pdf", "page": 1, "id": "multi_column_miss_04", "type": "absent", "text": "Downloaded from http://tobaccocontrol.bmj.com/", "threshold": 0.95} +{"pdf": "multi_column_miss.pdf", "page": 1, "id": "multi_column_miss_00", "type": "present", "text": "Corporate social responsibility and the tobacco industry: hope or hype?"} +{"pdf": "multi_column_miss.pdf", "page": 1, "id": "multi_column_miss_01", "type": "present", "text": "this leaves BAT to argue why it should not be held to be largely accountable for the annual deaths of some 754 600 smokers, and Philip Morris some 803 600 smokers."} +{"pdf": "multi_column_miss.pdf", "page": 1, "id": "multi_column_miss_02", "type": "present", "text": "The term \"corporate social responsibility\" is in vogue at the moment but as a concept it is vague and means different things to different people.", "max_diffs": 2} +{"pdf": "multi_column_miss.pdf", "page": 1, "id": "multi_column_miss_03", "type": "present", "text": "Over the past three decades increasing pressure from non-governmental"} +{"pdf": "multi_column_miss.pdf", "page": 1, "id": "multi_column_miss_04", "type": "absent", "text": "Downloaded from http://tobaccocontrol.bmj.com/"} {"pdf": "multi_column_miss.pdf", "page": 1, "id": "multi_column_miss_10", "type": "order", "before": "Corporate social responsibility and the tobacco industry: hope or hype?", "after": "The unprecedented expansion of power and influence of TNCs over the past three decades has accelerated global trade and development, but also environmental damage and abuses of", "threshold": 0.95} -{"pdf": "multi_column_miss.pdf", "page": 1, "id": "multi_column_miss_11", "type": "order", "before": "It now looks like that with vigilance", "after": "this leaves BAT to argue why it should not be held to be largely accountable for the annual deaths", "threshold": 0.95} +{"pdf": "multi_column_miss.pdf", "page": 1, "id": "multi_column_miss_11", "type": "order", "before": "It now looks like that with vigilance", "after": "this leaves BAT to argue why it should not be held to be largely accountable for the annual deaths", "max_diffs": 2} {"pdf": "multi_column_miss.pdf", "page": 1, "id": "multi_column_miss_12", "type": "order", "before": "Corporate social responsibility (CSR) emerged from a realisation among transnational corporations", "after": " perspective on its own behaviour; and reflects on whether marketing tobacco is antithetical to social responsibility.", "threshold": 0.95} -{"pdf": "discoverworld_crazy_table4.pdf", "page": 1, "id": "discoverworld_crazy_table4_00", "type": "present", "text": "Table 4: Baseline model performance on each of the three scoring metrics", "threshold": 1.0} -{"pdf": "discoverworld_crazy_table4.pdf", "page": 1, "id": "discoverworld_crazy_table4_01", "type": "present", "text": "Table 5: Baseline model performance on each of the three scoring metrics", "threshold": 1.0} -{"pdf": "discoverworld_crazy_table4.pdf", "page": 1, "id": "discoverworld_crazy_table4_02", "type": "present", "text": "We use the GPT-4O model for all our agents due to its higher performance and lower cost compared to other models. For space we provide", "threshold": 0.99} +{"pdf": "discoverworld_crazy_table4.pdf", "page": 1, "id": "discoverworld_crazy_table4_00", "type": "present", "text": "Table 4: Baseline model performance on each of the three scoring metrics"} +{"pdf": "discoverworld_crazy_table4.pdf", "page": 1, "id": "discoverworld_crazy_table4_01", "type": "present", "text": "Table 5: Baseline model performance on each of the three scoring metrics"} +{"pdf": "discoverworld_crazy_table4.pdf", "page": 1, "id": "discoverworld_crazy_table4_02", "type": "present", "text": "We use the GPT-4O model for all our agents due to its higher performance and lower cost compared to other models. For space we provide"} -{"pdf": "mattsnotes.pdf", "page": 1, "id": "mattsnotes_minediff_00", "type": "present", "threshold": 1, "checked": "verified", "text": "The-Stack-V2"} -{"pdf": "mattsnotes.pdf", "page": 1, "id": "mattsnotes_minediff_01", "type": "present", "threshold": 1, "checked": "verified", "text": "SE, whatever we've scraped"} -{"pdf": "mattsnotes.pdf", "page": 1, "id": "mattsnotes_minediff_02", "type": "present", "threshold": 1, "checked": "verified", "text": "HQ DCLM"} +{"pdf": "mattsnotes.pdf", "page": 1, "id": "mattsnotes_minediff_00", "type": "present", "checked": "verified", "text": "The-Stack-V2"} +{"pdf": "mattsnotes.pdf", "page": 1, "id": "mattsnotes_minediff_01", "type": "present", "checked": "verified", "text": "SE, whatever we've scraped"} +{"pdf": "mattsnotes.pdf", "page": 1, "id": "mattsnotes_minediff_02", "type": "present", "checked": "verified", "text": "HQ DCLM"} -{"pdf": "lincoln_letter.pdf", "page": 1, "id": "lincoln_letter_minediff_00", "type": "present", "threshold": 1, "checked": "verified", "text": "January 10th 1864."} -{"pdf": "lincoln_letter.pdf", "page": 1, "id": "lincoln_letter_minediff_01", "type": "present", "threshold": 1, "checked": "verified", "text": "Major General Hitchcock, Commissioner of Exchanges, is authorized and directed to offer Brigadier General Trimble, now a prisoner of war in Fort McHenry, in exchange for Major White, who is held as a prisoner at Richmond."} -{"pdf": "lincoln_letter.pdf", "page": 1, "id": "lincoln_letter_minediff_03", "type": "present", "threshold": 1, "checked": "verified", "text": "He is also directed to send forward the offer of exchange by Henry M. Warfield, Esq. of Baltimore, under a flag of truce, and give him a pass to City Point."} +{"pdf": "lincoln_letter.pdf", "page": 1, "id": "lincoln_letter_minediff_00", "type": "present", "checked": "verified", "text": "January 10th 1864."} +{"pdf": "lincoln_letter.pdf", "page": 1, "id": "lincoln_letter_minediff_01", "type": "present", "checked": "verified", "text": "Major General Hitchcock, Commissioner of Exchanges, is authorized and directed to offer Brigadier General Trimble, now a prisoner of war in Fort McHenry, in exchange for Major White, who is held as a prisoner at Richmond."} +{"pdf": "lincoln_letter.pdf", "page": 1, "id": "lincoln_letter_minediff_03", "type": "present", "checked": "verified", "text": "He is also directed to send forward the offer of exchange by Henry M. Warfield, Esq. of Baltimore, under a flag of truce, and give him a pass to City Point."} -{"pdf": "openstax_caculus_pg_273.pdf", "page": 1, "id": "openstax_caculus_pg_273_minediff_02", "type": "present", "threshold": 1, "checked": "verified", "text": "Use the graph of the position function to determine the time intervals when the velocity is positive, negative, or zero."} -{"pdf": "openstax_caculus_pg_273.pdf", "page": 1, "id": "openstax_caculus_pg_273_minediff_03", "type": "present", "threshold": 1, "checked": "verified", "text": "Use the graph of the velocity function to determine the time intervals when the acceleration is positive, negative, or zero."} +{"pdf": "openstax_caculus_pg_273.pdf", "page": 1, "id": "openstax_caculus_pg_273_minediff_02", "type": "present", "checked": "verified", "text": "Use the graph of the position function to determine the time intervals when the velocity is positive, negative, or zero."} +{"pdf": "openstax_caculus_pg_273.pdf", "page": 1, "id": "openstax_caculus_pg_273_minediff_03", "type": "present", "checked": "verified", "text": "Use the graph of the velocity function to determine the time intervals when the acceleration is positive, negative, or zero."} -{"pdf": "multi_column_miss.pdf", "page": 1, "id": "multi_column_miss_minediff_01", "type": "present", "threshold": 1, "checked": "verified", "text": "This report first provides the context and development of CSR; then, from internal company documents, examines how PM came to its own version."} -{"pdf": "multi_column_miss.pdf", "page": 1, "id": "multi_column_miss_minediff_02", "type": "present", "threshold": 1, "checked": "verified", "text": "This paper examines whether a tobacco company espousing CSR should be judged simply as a corporate entity along standards of business ethics, or as an irretrievably negative force in the realm of public health, thereby rendering CSR an oxymoron."} +{"pdf": "multi_column_miss.pdf", "page": 1, "id": "multi_column_miss_minediff_01", "type": "present", "checked": "verified", "text": "This report first provides the context and development of CSR; then, from internal company documents, examines how PM came to its own version."} +{"pdf": "multi_column_miss.pdf", "page": 1, "id": "multi_column_miss_minediff_02", "type": "present", "checked": "verified", "text": "This paper examines whether a tobacco company espousing CSR should be judged simply as a corporate entity along standards of business ethics, or as an irretrievably negative force in the realm of public health, thereby rendering CSR an oxymoron."} + +{"pdf": "olmo2-pg4.pdf", "page": 1, "id": "olmo2-pg4_minediff_00", "type": "present", "checked": "verified", "text": "Table 1 Composition of the pretraining data for OLMo 2."} + +{"pdf": "olmo2-pg4.pdf", "page": 1, "id": "olmo2-pg4_table00", "type": "table", "cell": "Type"} +{"pdf": "olmo2-pg4.pdf", "page": 1, "id": "olmo2-pg4_table00", "type": "table", "cell": "3.32T", "left": "3.71T"} +{"pdf": "olmo2-pg4.pdf", "page": 1, "id": "olmo2-pg4_table00", "type": "table", "cell": "3.32T", "right": "21.32T"} +{"pdf": "olmo2-pg4.pdf", "page": 1, "id": "olmo2-pg4_table00", "type": "table", "cell": "11.8B", "up": "12.2B"} +{"pdf": "olmo2-pg4.pdf", "page": 1, "id": "olmo2-pg4_table00", "type": "table", "cell": "11.8B", "down": "3.7B"} +{"pdf": "olmo2-pg4.pdf", "page": 1, "id": "olmo2-pg4_table00", "type": "table", "cell": "3.32T", "top_heading": "Words"} +{"pdf": "olmo2-pg4.pdf", "page": 1, "id": "olmo2-pg4_table00", "type": "table", "cell": "arXiv", "top_heading": "Source"} +{"pdf": "olmo2-pg4.pdf", "page": 1, "id": "olmo2-pg4_table00", "type": "table", "cell": "47.2B", "top_heading": "Bytes"} +{"pdf": "olmo2-pg4.pdf", "page": 1, "id": "olmo2-pg4_table00", "type": "table", "cell": "Math proofs code", "left_heading": "Algebraic Stack"} -{"pdf": "olmo2-pg4.pdf", "page": 1, "id": "olmo2-pg4_minediff_00", "type": "present", "threshold": 1, "checked": "verified", "text": "Table 1 Composition of the pretraining data for OLMo 2."} \ No newline at end of file diff --git a/olmocr/bench/synth/render.py b/olmocr/bench/synth/render.py index c8d1647..551cf01 100644 --- a/olmocr/bench/synth/render.py +++ b/olmocr/bench/synth/render.py @@ -6,7 +6,7 @@ from playwright.async_api import async_playwright # Simple configuration CONFIG = { - "input_file": os.path.join(os.path.dirname(__file__), "templates", "bookpage.js"), # React component file + "input_file": os.path.join(os.path.dirname(__file__), "templates", "listpage.js"), # React component file "output_pdf": "book-page.pdf", # Output PDF filename "temp_html": "temp-render.html", # Temporary HTML file "wait_time": 1500, # Time to wait for rendering (ms) diff --git a/olmocr/bench/synth/templates/listpage.js b/olmocr/bench/synth/templates/listpage.js new file mode 100644 index 0000000..e2313d3 --- /dev/null +++ b/olmocr/bench/synth/templates/listpage.js @@ -0,0 +1,83 @@ +//import React from 'react'; + +const PermitGuidelinesTemplate = () => { + // Sample data - you can replace these with your own + const guidelineItems = [ + { + number: 'iii.', + content: 'Not rely on personal preference or opinion, or regional interpretation of statute, regulation or guidance that is inconsistent with the Department\'s statewide interpretation. Staff should confer with the appropriate Bureau Director as necessary.' + }, + { + number: 'iv.', + content: 'Process technically adequate and scientifically sound applications for final approval to minimize elapsed time in accordance with the Permit Decision Guarantee.' + }, + { + number: 'v.', + content: 'Where the Application Manager determines that the technical information submitted with the application does not meet technical guidance or standards published by the Department, the application must provide the scientific or engineering basis to support the application. Note that deviations from technical guidance can generally be approved, by the appropriate section chief and manager, when warranted, provided acceptable justification has been submitted. Minor deficiencies that can be easily corrected should be addressed through a telephone call with the applicant and consultant, and may negate the need for a deficiency letter. The Program Manager or District Manager will be responsible for making that decision.' + }, + { + number: 'vi.', + content: 'If an application fails to provide the technical information necessary to document that applicable regulatory and statutory requirements will be achieved, it is technically deficient and the Application Manager will prepare a technical deficiency letter. Again, all deficiencies noted must cite the statutory or regulatory obligation that the application has failed to meet and the Section Chief and the Program Manager will routinely review these letters. For District Oil and Gas Offices and District Mining Offices the Permits Chief and the Manager will review the letters.' + }, + { + number: 'vii.', + content: 'Applicant responses that do not make the application technically adequate within the established response timeframe will be subject to the Elevated Review Process below. Applications that are made technically adequate within the established response timeframe will proceed to processing for final action.' + } + ]; + + // Footnote data + const footnote = { + number: '2', + content: 'More technically complex projects and applications may receive additional deficiency letters as appropriate prior to a decision point. This exception will not void inclusion in the Permit Decision Guarantee and will follow program specific guidance that is developed. The more technically complex projects and applications are noted with an asterisk ("*") in Appendix A.' + }; + + // Document info + const documentInfo = "021-2100-001 / November 2, 2012 / Page 11"; + + // Special note about technical deficiency letter + const technicalDeficiencyNote = { + prefix: 'One', + superscript: '2', + content: ' technical deficiency letter will be sent. Each deficiency cited must note the statute, regulation or technical guidance provision. Technical guidance provides a means to compliance, but may not be used or cited when issuing a permit denial. The letter will state, as necessary, that the Permit Decision Guarantee is no longer applicable and offer the applicant an opportunity to meet and discuss the deficiencies. The letter will include a deadline for submission of the deficient information.' + }; + + return ( +
+
+ {guidelineItems.map((item, index) => ( +
+
{item.number}
+
{item.content}
+
+ ))} + + {/* Technical deficiency letter note */} +
+

+ {technicalDeficiencyNote.prefix} + {technicalDeficiencyNote.superscript} + {technicalDeficiencyNote.content} +

+
+
+ + {/* Horizontal line */} +
+ + {/* Footnote section */} +
+

+ {footnote.number} {footnote.content} +

+
+ + {/* Document info */} +
+ {documentInfo} +
+
+ ); +}; + +//export default PermitGuidelinesTemplate; +window.BookPageTemplate = PermitGuidelinesTemplate; \ No newline at end of file diff --git a/olmocr/bench/tests.py b/olmocr/bench/tests.py index 749221e..d6279f8 100644 --- a/olmocr/bench/tests.py +++ b/olmocr/bench/tests.py @@ -1,4 +1,7 @@ import json +import re +import numpy as np + from dataclasses import asdict, dataclass from enum import Enum from typing import List, Optional, Tuple @@ -11,6 +14,7 @@ class TestType(str, Enum): PRESENT = "present" ABSENT = "absent" ORDER = "order" + TABLE = "table" class TestChecked(str, Enum): @@ -41,18 +45,16 @@ class BasePDFTest: page: int id: str type: str - threshold: float = 1.0 + max_diffs: int = 0 checked: Optional[TestChecked] = None def __post_init__(self): - self.threshold = float(self.threshold) - if not self.pdf: raise ValidationError("PDF filename cannot be empty") if not self.id: raise ValidationError("Test ID cannot be empty") - if not isinstance(self.threshold, float) or not (0 <= self.threshold <= 1): - raise ValidationError(f"Threshold must be a float between 0 and 1, got {self.threshold}") + if not isinstance(self.max_diffs, int) or self.max_diffs < 0: + raise ValidationError(f"Max diffs must be positive number or 0") if self.type not in {t.value for t in TestType}: raise ValidationError(f"Invalid test type: {self.type}") @@ -90,7 +92,8 @@ class TextPresenceTest(BasePDFTest): def run(self, md_content: str) -> Tuple[bool, str]: reference_query = self.text - threshold = self.threshold + # Threshold for fuzzy matching derived from max_diffs + threshold = 1.0 - (self.max_diffs / (len(reference_query) if len(reference_query) > 0 else 1)) best_ratio = fuzz.partial_ratio(reference_query, md_content) / 100.0 if self.type == TestType.PRESENT.value: @@ -130,15 +133,13 @@ class TextOrderTest(BasePDFTest): raise ValidationError("After field cannot be empty") def run(self, md_content: str) -> Tuple[bool, str]: - threshold = self.threshold - max_l_dist = round((1.0 - threshold) * len(self.before)) - before_matches = find_near_matches(self.before, md_content, max_l_dist=max_l_dist) - after_matches = find_near_matches(self.after, md_content, max_l_dist=max_l_dist) + before_matches = find_near_matches(self.before, md_content, max_l_dist=self.max_diffs) + after_matches = find_near_matches(self.after, md_content, max_l_dist=self.max_diffs) if not before_matches: - return False, f"'before' text '{self.before[:40]}...' not found with max_l_dist {max_l_dist}" + return False, f"'before' text '{self.before[:40]}...' not found with max_l_dist {self.max_diffs}" if not after_matches: - return False, f"'after' text '{self.after[:40]}...' not found with max_l_dist {max_l_dist}" + return False, f"'after' text '{self.after[:40]}...' not found with max_l_dist {self.max_diffs}" for before_match in before_matches: for after_match in after_matches: @@ -146,6 +147,165 @@ class TextOrderTest(BasePDFTest): return True, "" return False, (f"Could not find a location where '{self.before[:40]}...' appears before " f"'{self.after[:40]}...'.") +@dataclass +class TableTest(BasePDFTest): + """ + Test to verify certain properties of a table are held, namely that some cells appear relative to other cells correctly + """ + # This is the target cell, which must exist in at least one place in the table + cell: str + + # These properties say that the cell immediately up/down/left/right of the target cell has the string specified + up: str="" + down: str="" + left: str="" + right: str="" + + # These properties say that the cell all the way up, or all the way left of the target cell (ex. headings) has the string value specified + top_heading: str="" + left_heading: str="" + + def __post_init__(self): + super().__post_init__() + if self.type != TestType.TABLE.value: + raise ValidationError(f"Invalid type for TabelText: {self.type}") + + def run(self, md_content: str) -> Tuple[bool, str]: + """ + Run the table test on provided markdown content. + + Finds all markdown tables and checks if any cell matches the target cell + and satisfies the specified relationships (up, down, left, right, headings). + + Args: + md_content: The markdown content containing tables + + Returns: + A tuple (passed, explanation) where 'passed' is True if at least one cell + passes all the specified relationships, and 'explanation' provides details when the test fails. + """ + # Threshold for fuzzy matching derived from max_diffs + threshold = 1.0 - (self.max_diffs / (len(self.cell) if len(self.cell) > 0 else 1)) + + # Extract all tables from markdown + table_pattern = r'(\|(?:[^|]*\|)+)\s*\n\|(?:[:-]+\|)+\s*\n((?:\|(?:[^|]*\|)+\s*\n)+)' + table_matches = re.finditer(table_pattern, md_content) + failed_reasons = [] + + for table_match in table_matches: + # Extract header and body from the table match + header_row = table_match.group(1).strip() + body_rows = table_match.group(2).strip().split('\n') + + # Process header and rows to remove leading/trailing | + header_cells = [cell.strip() for cell in header_row.split('|')] + if header_cells[0] == '': + header_cells = header_cells[1:] + if header_cells[-1] == '': + header_cells = header_cells[:-1] + + # Process table body rows + table_data = [] + for row in [header_row] + body_rows: + if '|' not in row: # Skip separator row + continue + + cells = [cell.strip() for cell in row.split('|')] + if cells[0] == '': + cells = cells[1:] + if cells[-1] == '': + cells = cells[:-1] + + table_data.append(cells) + + # Skip separator row (second row with dashes) + if len(table_data) > 1 and all('-' in cell for cell in table_data[1]): + table_data = [table_data[0]] + table_data[2:] + + # Convert to numpy array for easier manipulation + # First ensure all rows have the same number of columns by padding if necessary + max_cols = max(len(row) for row in table_data) + padded_data = [row + [''] * (max_cols - len(row)) for row in table_data] + table_array = np.array(padded_data) + + # Find all cells that match the target cell using fuzzy matching + matches = [] + for i in range(table_array.shape[0]): + for j in range(table_array.shape[1]): + cell_content = table_array[i, j] + similarity = fuzz.ratio(self.cell, cell_content) / 100.0 + + if similarity >= threshold: + matches.append((i, j)) + + # If no matches found in this table, continue to the next table + if not matches: + continue + + # Check the relationships for each matching cell + for row_idx, col_idx in matches: + all_relationships_satisfied = True + failed_reasons = [] + + # Check up relationship + if self.up and row_idx > 0: + up_cell = table_array[row_idx - 1, col_idx] + up_similarity = fuzz.ratio(self.up, up_cell) / 100.0 + if up_similarity < threshold: + all_relationships_satisfied = False + failed_reasons.append(f"Cell above '{up_cell}' doesn't match expected '{self.up}' (similarity: {up_similarity:.2f})") + + # Check down relationship + if self.down and row_idx < table_array.shape[0] - 1: + down_cell = table_array[row_idx + 1, col_idx] + down_similarity = fuzz.ratio(self.down, down_cell) / 100.0 + if down_similarity < threshold: + all_relationships_satisfied = False + failed_reasons.append(f"Cell below '{down_cell}' doesn't match expected '{self.down}' (similarity: {down_similarity:.2f})") + + # Check left relationship + if self.left and col_idx > 0: + left_cell = table_array[row_idx, col_idx - 1] + left_similarity = fuzz.ratio(self.left, left_cell) / 100.0 + if left_similarity < threshold: + all_relationships_satisfied = False + failed_reasons.append(f"Cell to the left '{left_cell}' doesn't match expected '{self.left}' (similarity: {left_similarity:.2f})") + + # Check right relationship + if self.right and col_idx < table_array.shape[1] - 1: + right_cell = table_array[row_idx, col_idx + 1] + right_similarity = fuzz.ratio(self.right, right_cell) / 100.0 + if right_similarity < threshold: + all_relationships_satisfied = False + failed_reasons.append(f"Cell to the right '{right_cell}' doesn't match expected '{self.right}' (similarity: {right_similarity:.2f})") + + # Check top heading relationship + if self.top_heading and row_idx > 0: + # The top heading is the cell in the first row with the same column + top_heading_cell = table_array[0, col_idx] + top_similarity = fuzz.ratio(self.top_heading, top_heading_cell) / 100.0 + if top_similarity < threshold: + all_relationships_satisfied = False + failed_reasons.append(f"Top heading '{top_heading_cell}' doesn't match expected '{self.top_heading}' (similarity: {top_similarity:.2f})") + + # Check left heading relationship + if self.left_heading and col_idx > 0: + # The left heading is the cell in the first column with the same row + left_heading_cell = table_array[row_idx, 0] + left_heading_similarity = fuzz.ratio(self.left_heading, left_heading_cell) / 100.0 + if left_heading_similarity < threshold: + all_relationships_satisfied = False + failed_reasons.append(f"Left heading '{left_heading_cell}' doesn't match expected '{self.left_heading}' (similarity: {left_heading_similarity:.2f})") + + # If all relationships are satisfied for this cell, the test passes + if all_relationships_satisfied: + return True, "" + + # If we've gone through all tables and all matching cells and none satisfied all relationships + if not failed_reasons: + return False, f"No cell matching '{self.cell}' found in any table with threshold {threshold}" + else: + return False, f"Found cells matching '{self.cell}' but relationships were not satisfied: {'; '.join(failed_reasons)}" def load_tests(jsonl_file: str) -> List[BasePDFTest]: """ @@ -171,6 +331,8 @@ def load_tests(jsonl_file: str) -> List[BasePDFTest]: test = TextPresenceTest(**data) elif test_type == TestType.ORDER.value: test = TextOrderTest(**data) + elif test_type == TestType.TABLE.value: + test = TableTest(**data) else: raise ValidationError(f"Unknown test type: {test_type}") From 3a0bcb6afd9947e1189c1458f668259bbd2b7fdd Mon Sep 17 00:00:00 2001 From: Jake Poznanski Date: Tue, 4 Mar 2025 14:04:50 -0800 Subject: [PATCH 6/8] Better table tests --- olmocr/bench/sample_data/dataset.jsonl | 32 ++++++++++++++------- olmocr/bench/tests.py | 39 +++++++++++++++++++------- 2 files changed, 51 insertions(+), 20 deletions(-) diff --git a/olmocr/bench/sample_data/dataset.jsonl b/olmocr/bench/sample_data/dataset.jsonl index 820262e..3b4d397 100644 --- a/olmocr/bench/sample_data/dataset.jsonl +++ b/olmocr/bench/sample_data/dataset.jsonl @@ -4,9 +4,9 @@ {"pdf": "multi_column_miss.pdf", "page": 1, "id": "multi_column_miss_03", "type": "present", "text": "Over the past three decades increasing pressure from non-governmental"} {"pdf": "multi_column_miss.pdf", "page": 1, "id": "multi_column_miss_04", "type": "absent", "text": "Downloaded from http://tobaccocontrol.bmj.com/"} -{"pdf": "multi_column_miss.pdf", "page": 1, "id": "multi_column_miss_10", "type": "order", "before": "Corporate social responsibility and the tobacco industry: hope or hype?", "after": "The unprecedented expansion of power and influence of TNCs over the past three decades has accelerated global trade and development, but also environmental damage and abuses of", "threshold": 0.95} +{"pdf": "multi_column_miss.pdf", "page": 1, "id": "multi_column_miss_10", "type": "order", "before": "Corporate social responsibility and the tobacco industry: hope or hype?", "after": "The unprecedented expansion of power and influence of TNCs over the past three decades has accelerated global trade and development, but also environmental damage and abuses of", "max_diffs": 2} {"pdf": "multi_column_miss.pdf", "page": 1, "id": "multi_column_miss_11", "type": "order", "before": "It now looks like that with vigilance", "after": "this leaves BAT to argue why it should not be held to be largely accountable for the annual deaths", "max_diffs": 2} -{"pdf": "multi_column_miss.pdf", "page": 1, "id": "multi_column_miss_12", "type": "order", "before": "Corporate social responsibility (CSR) emerged from a realisation among transnational corporations", "after": " perspective on its own behaviour; and reflects on whether marketing tobacco is antithetical to social responsibility.", "threshold": 0.95} +{"pdf": "multi_column_miss.pdf", "page": 1, "id": "multi_column_miss_12", "type": "order", "before": "Corporate social responsibility (CSR) emerged from a realisation among transnational corporations", "after": " perspective on its own behaviour; and reflects on whether marketing tobacco is antithetical to social responsibility.", "max_diffs": 2} {"pdf": "discoverworld_crazy_table4.pdf", "page": 1, "id": "discoverworld_crazy_table4_00", "type": "present", "text": "Table 4: Baseline model performance on each of the three scoring metrics"} {"pdf": "discoverworld_crazy_table4.pdf", "page": 1, "id": "discoverworld_crazy_table4_01", "type": "present", "text": "Table 5: Baseline model performance on each of the three scoring metrics"} @@ -29,12 +29,24 @@ {"pdf": "olmo2-pg4.pdf", "page": 1, "id": "olmo2-pg4_minediff_00", "type": "present", "checked": "verified", "text": "Table 1 Composition of the pretraining data for OLMo 2."} {"pdf": "olmo2-pg4.pdf", "page": 1, "id": "olmo2-pg4_table00", "type": "table", "cell": "Type"} -{"pdf": "olmo2-pg4.pdf", "page": 1, "id": "olmo2-pg4_table00", "type": "table", "cell": "3.32T", "left": "3.71T"} -{"pdf": "olmo2-pg4.pdf", "page": 1, "id": "olmo2-pg4_table00", "type": "table", "cell": "3.32T", "right": "21.32T"} -{"pdf": "olmo2-pg4.pdf", "page": 1, "id": "olmo2-pg4_table00", "type": "table", "cell": "11.8B", "up": "12.2B"} -{"pdf": "olmo2-pg4.pdf", "page": 1, "id": "olmo2-pg4_table00", "type": "table", "cell": "11.8B", "down": "3.7B"} -{"pdf": "olmo2-pg4.pdf", "page": 1, "id": "olmo2-pg4_table00", "type": "table", "cell": "3.32T", "top_heading": "Words"} -{"pdf": "olmo2-pg4.pdf", "page": 1, "id": "olmo2-pg4_table00", "type": "table", "cell": "arXiv", "top_heading": "Source"} -{"pdf": "olmo2-pg4.pdf", "page": 1, "id": "olmo2-pg4_table00", "type": "table", "cell": "47.2B", "top_heading": "Bytes"} -{"pdf": "olmo2-pg4.pdf", "page": 1, "id": "olmo2-pg4_table00", "type": "table", "cell": "Math proofs code", "left_heading": "Algebraic Stack"} +{"pdf": "olmo2-pg4.pdf", "page": 1, "id": "olmo2-pg4_table01", "type": "table", "cell": "3.32T", "left": "3.71T"} +{"pdf": "olmo2-pg4.pdf", "page": 1, "id": "olmo2-pg4_table02", "type": "table", "cell": "3.32T", "right": "21.32T"} +{"pdf": "olmo2-pg4.pdf", "page": 1, "id": "olmo2-pg4_table03", "type": "table", "cell": "11.8B", "up": "12.2B"} +{"pdf": "olmo2-pg4.pdf", "page": 1, "id": "olmo2-pg4_table04", "type": "table", "cell": "11.8B", "down": "3.7B"} +{"pdf": "olmo2-pg4.pdf", "page": 1, "id": "olmo2-pg4_table05", "type": "table", "cell": "3.32T", "top_heading": "Words"} +{"pdf": "olmo2-pg4.pdf", "page": 1, "id": "olmo2-pg4_table06", "type": "table", "cell": "arXiv", "top_heading": "Source"} +{"pdf": "olmo2-pg4.pdf", "page": 1, "id": "olmo2-pg4_table07", "type": "table", "cell": "47.2B", "top_heading": "Bytes"} +{"pdf": "olmo2-pg4.pdf", "page": 1, "id": "olmo2-pg4_table08", "type": "table", "cell": "Math proofs code", "left_heading": "Algebraic Stack"} + +{"pdf": "discoverworld_crazy_table4.pdf", "page": 1, "id": "olmo2-discoverworld_crazy_table4_t00", "type": "table", "cell": "Quadratic regression", "left": "Challenge"} +{"pdf": "discoverworld_crazy_table4.pdf", "page": 1, "id": "olmo2-discoverworld_crazy_table4_t00", "type": "table", "cell": "Instrument Use", "left": "Normal"} +{"pdf": "discoverworld_crazy_table4.pdf", "page": 1, "id": "olmo2-discoverworld_crazy_table4_t00", "type": "table", "cell": "0.87", "top_heading": "Procedure"} +{"pdf": "discoverworld_crazy_table4.pdf", "page": 1, "id": "olmo2-discoverworld_crazy_table4_t00", "type": "table", "cell": "0.87", "top_heading": "ReACT"} + +{"pdf": "discoverworld_crazy_table4.pdf", "page": 1, "id": "olmo2-discoverworld_crazy_table4_t00", "type": "table", "cell": "Pick-and-place object", "left_heading": "27"} +{"pdf": "discoverworld_crazy_table4.pdf", "page": 1, "id": "olmo2-discoverworld_crazy_table4_t00", "type": "table", "cell": "0.66", "right": "0.44"} + +{"pdf": "discoverworld_crazy_table4.pdf", "page": 1, "id": "olmo2-discoverworld_crazy_table4_t00", "type": "table", "cell": "Interact with a moving agent", "top_heading": "Unit Test Topic"} + + diff --git a/olmocr/bench/tests.py b/olmocr/bench/tests.py index d6279f8..2864ee6 100644 --- a/olmocr/bench/tests.py +++ b/olmocr/bench/tests.py @@ -281,21 +281,40 @@ class TableTest(BasePDFTest): # Check top heading relationship if self.top_heading and row_idx > 0: - # The top heading is the cell in the first row with the same column - top_heading_cell = table_array[0, col_idx] - top_similarity = fuzz.ratio(self.top_heading, top_heading_cell) / 100.0 - if top_similarity < threshold: + # Find the first non-empty cell in the same column (starting from the top) + top_heading_cell = "" + for i in range(row_idx): + if table_array[i, col_idx].strip(): + top_heading_cell = table_array[i, col_idx] + break + + if not top_heading_cell: all_relationships_satisfied = False - failed_reasons.append(f"Top heading '{top_heading_cell}' doesn't match expected '{self.top_heading}' (similarity: {top_similarity:.2f})") + failed_reasons.append(f"No non-empty top heading found in column {col_idx}") + else: + top_similarity = fuzz.ratio(self.top_heading, top_heading_cell) / 100.0 + if top_similarity < threshold: + all_relationships_satisfied = False + failed_reasons.append(f"Top heading '{top_heading_cell}' doesn't match expected '{self.top_heading}' (similarity: {top_similarity:.2f})") # Check left heading relationship if self.left_heading and col_idx > 0: - # The left heading is the cell in the first column with the same row - left_heading_cell = table_array[row_idx, 0] - left_heading_similarity = fuzz.ratio(self.left_heading, left_heading_cell) / 100.0 - if left_heading_similarity < threshold: + # Find the first non-empty cell in the same row (starting from the left) + left_heading_cell = "" + for j in range(col_idx): + if table_array[row_idx, j].strip(): + left_heading_cell = table_array[row_idx, j] + break + + if not left_heading_cell: all_relationships_satisfied = False - failed_reasons.append(f"Left heading '{left_heading_cell}' doesn't match expected '{self.left_heading}' (similarity: {left_heading_similarity:.2f})") + failed_reasons.append(f"No non-empty left heading found in row {row_idx}") + else: + left_heading_similarity = fuzz.ratio(self.left_heading, left_heading_cell) / 100.0 + if left_heading_similarity < threshold: + all_relationships_satisfied = False + failed_reasons.append(f"Left heading '{left_heading_cell}' doesn't match expected '{self.left_heading}' (similarity: {left_heading_similarity:.2f})") + # If all relationships are satisfied for this cell, the test passes if all_relationships_satisfied: From 004486f014c4c308a8720afbb7046d51c58f0b54 Mon Sep 17 00:00:00 2001 From: Jake Poznanski Date: Tue, 4 Mar 2025 14:22:03 -0800 Subject: [PATCH 7/8] Nice tables support --- olmocr/bench/tests.py | 209 ++++++++++++++++++++++++++++-------------- 1 file changed, 142 insertions(+), 67 deletions(-) diff --git a/olmocr/bench/tests.py b/olmocr/bench/tests.py index 2864ee6..fd56bf6 100644 --- a/olmocr/bench/tests.py +++ b/olmocr/bench/tests.py @@ -1,15 +1,107 @@ import json import re import numpy as np +from bs4 import BeautifulSoup from dataclasses import asdict, dataclass from enum import Enum -from typing import List, Optional, Tuple +from typing import List, Optional, Tuple, Dict, Any from fuzzysearch import find_near_matches from rapidfuzz import fuzz +def parse_markdown_tables(md_content: str) -> List[np.ndarray]: + """ + Extract and parse all markdown tables from the provided content. + + Args: + md_content: The markdown content containing tables + + Returns: + A list of numpy arrays, each representing a parsed table + """ + # Extract all tables from markdown + table_pattern = r'(\|(?:[^|]*\|)+)\s*\n\|(?:[:-]+\|)+\s*\n((?:\|(?:[^|]*\|)+\s*\n)+)' + table_matches = re.finditer(table_pattern, md_content) + + parsed_tables = [] + + for table_match in table_matches: + # Extract header and body from the table match + header_row = table_match.group(1).strip() + body_rows = table_match.group(2).strip().split('\n') + + # Process header and rows to remove leading/trailing | + header_cells = [cell.strip() for cell in header_row.split('|')] + if header_cells[0] == '': + header_cells = header_cells[1:] + if header_cells[-1] == '': + header_cells = header_cells[:-1] + + # Process table body rows + table_data = [] + for row in [header_row] + body_rows: + if '|' not in row: # Skip separator row + continue + + cells = [cell.strip() for cell in row.split('|')] + if cells[0] == '': + cells = cells[1:] + if cells[-1] == '': + cells = cells[:-1] + + table_data.append(cells) + + # Skip separator row (second row with dashes) + if len(table_data) > 1 and all('-' in cell for cell in table_data[1]): + table_data = [table_data[0]] + table_data[2:] + + # Convert to numpy array for easier manipulation + # First ensure all rows have the same number of columns by padding if necessary + max_cols = max(len(row) for row in table_data) + padded_data = [row + [''] * (max_cols - len(row)) for row in table_data] + table_array = np.array(padded_data) + + parsed_tables.append(table_array) + + return parsed_tables + + +def parse_html_tables(html_content: str) -> List[np.ndarray]: + """ + Extract and parse all HTML tables from the provided content. + + Args: + html_content: The HTML content containing tables + + Returns: + A list of numpy arrays, each representing a parsed table + """ + soup = BeautifulSoup(html_content, 'html.parser') + tables = soup.find_all('table') + + parsed_tables = [] + + for table in tables: + rows = table.find_all(['tr']) + table_data = [] + + for row in rows: + cells = row.find_all(['th', 'td']) + row_data = [cell.get_text().strip() for cell in cells] + table_data.append(row_data) + + # Ensure all rows have the same number of columns + if table_data: + max_cols = max(len(row) for row in table_data) + padded_data = [row + [''] * (max_cols - len(row)) for row in table_data] + table_array = np.array(padded_data) + parsed_tables.append(table_array) + + return parsed_tables + + class TestType(str, Enum): PRESENT = "present" ABSENT = "absent" @@ -147,87 +239,68 @@ class TextOrderTest(BasePDFTest): return True, "" return False, (f"Could not find a location where '{self.before[:40]}...' appears before " f"'{self.after[:40]}...'.") + + + + @dataclass class TableTest(BasePDFTest): """ - Test to verify certain properties of a table are held, namely that some cells appear relative to other cells correctly + Test to verify certain properties of a table are held, namely that some cells appear relative to other cells correctly """ # This is the target cell, which must exist in at least one place in the table cell: str # These properties say that the cell immediately up/down/left/right of the target cell has the string specified - up: str="" - down: str="" - left: str="" - right: str="" + up: str = "" + down: str = "" + left: str = "" + right: str = "" # These properties say that the cell all the way up, or all the way left of the target cell (ex. headings) has the string value specified - top_heading: str="" - left_heading: str="" + top_heading: str = "" + left_heading: str = "" + def __post_init__(self): super().__post_init__() if self.type != TestType.TABLE.value: - raise ValidationError(f"Invalid type for TabelText: {self.type}") + raise ValidationError(f"Invalid type for TableTest: {self.type}") - def run(self, md_content: str) -> Tuple[bool, str]: + def run(self, content: str) -> Tuple[bool, str]: """ - Run the table test on provided markdown content. + Run the table test on provided content. - Finds all markdown tables and checks if any cell matches the target cell - and satisfies the specified relationships (up, down, left, right, headings). + Finds all tables (markdown and/or HTML based on content_type) and checks if any cell + matches the target cell and satisfies the specified relationships. Args: - md_content: The markdown content containing tables + content: The content containing tables (markdown or HTML) Returns: - A tuple (passed, explanation) where 'passed' is True if at least one cell - passes all the specified relationships, and 'explanation' provides details when the test fails. + A tuple (passed, explanation) where 'passed' is True if the test passes, + and 'explanation' provides details when the test fails. """ + # Initialize variables to track tables and results + tables_to_check = [] + failed_reasons = [] + # Threshold for fuzzy matching derived from max_diffs threshold = 1.0 - (self.max_diffs / (len(self.cell) if len(self.cell) > 0 else 1)) - # Extract all tables from markdown - table_pattern = r'(\|(?:[^|]*\|)+)\s*\n\|(?:[:-]+\|)+\s*\n((?:\|(?:[^|]*\|)+\s*\n)+)' - table_matches = re.finditer(table_pattern, md_content) - failed_reasons = [] + # Parse tables based on content_type + md_tables = parse_markdown_tables(content) + tables_to_check.extend(md_tables) - for table_match in table_matches: - # Extract header and body from the table match - header_row = table_match.group(1).strip() - body_rows = table_match.group(2).strip().split('\n') - - # Process header and rows to remove leading/trailing | - header_cells = [cell.strip() for cell in header_row.split('|')] - if header_cells[0] == '': - header_cells = header_cells[1:] - if header_cells[-1] == '': - header_cells = header_cells[:-1] - - # Process table body rows - table_data = [] - for row in [header_row] + body_rows: - if '|' not in row: # Skip separator row - continue - - cells = [cell.strip() for cell in row.split('|')] - if cells[0] == '': - cells = cells[1:] - if cells[-1] == '': - cells = cells[:-1] - - table_data.append(cells) - - # Skip separator row (second row with dashes) - if len(table_data) > 1 and all('-' in cell for cell in table_data[1]): - table_data = [table_data[0]] + table_data[2:] - - # Convert to numpy array for easier manipulation - # First ensure all rows have the same number of columns by padding if necessary - max_cols = max(len(row) for row in table_data) - padded_data = [row + [''] * (max_cols - len(row)) for row in table_data] - table_array = np.array(padded_data) + html_tables = parse_html_tables(content) + tables_to_check.extend(html_tables) + + # If no tables found, return failure + if not tables_to_check: + return False, f"No tables found in the content at all" + # Check each table + for table_array in tables_to_check: # Find all cells that match the target cell using fuzzy matching matches = [] for i in range(table_array.shape[0]): @@ -245,7 +318,7 @@ class TableTest(BasePDFTest): # Check the relationships for each matching cell for row_idx, col_idx in matches: all_relationships_satisfied = True - failed_reasons = [] + current_failed_reasons = [] # Check up relationship if self.up and row_idx > 0: @@ -253,7 +326,7 @@ class TableTest(BasePDFTest): up_similarity = fuzz.ratio(self.up, up_cell) / 100.0 if up_similarity < threshold: all_relationships_satisfied = False - failed_reasons.append(f"Cell above '{up_cell}' doesn't match expected '{self.up}' (similarity: {up_similarity:.2f})") + current_failed_reasons.append(f"Cell above '{up_cell}' doesn't match expected '{self.up}' (similarity: {up_similarity:.2f})") # Check down relationship if self.down and row_idx < table_array.shape[0] - 1: @@ -261,7 +334,7 @@ class TableTest(BasePDFTest): down_similarity = fuzz.ratio(self.down, down_cell) / 100.0 if down_similarity < threshold: all_relationships_satisfied = False - failed_reasons.append(f"Cell below '{down_cell}' doesn't match expected '{self.down}' (similarity: {down_similarity:.2f})") + current_failed_reasons.append(f"Cell below '{down_cell}' doesn't match expected '{self.down}' (similarity: {down_similarity:.2f})") # Check left relationship if self.left and col_idx > 0: @@ -269,7 +342,7 @@ class TableTest(BasePDFTest): left_similarity = fuzz.ratio(self.left, left_cell) / 100.0 if left_similarity < threshold: all_relationships_satisfied = False - failed_reasons.append(f"Cell to the left '{left_cell}' doesn't match expected '{self.left}' (similarity: {left_similarity:.2f})") + current_failed_reasons.append(f"Cell to the left '{left_cell}' doesn't match expected '{self.left}' (similarity: {left_similarity:.2f})") # Check right relationship if self.right and col_idx < table_array.shape[1] - 1: @@ -277,7 +350,7 @@ class TableTest(BasePDFTest): right_similarity = fuzz.ratio(self.right, right_cell) / 100.0 if right_similarity < threshold: all_relationships_satisfied = False - failed_reasons.append(f"Cell to the right '{right_cell}' doesn't match expected '{self.right}' (similarity: {right_similarity:.2f})") + current_failed_reasons.append(f"Cell to the right '{right_cell}' doesn't match expected '{self.right}' (similarity: {right_similarity:.2f})") # Check top heading relationship if self.top_heading and row_idx > 0: @@ -290,12 +363,12 @@ class TableTest(BasePDFTest): if not top_heading_cell: all_relationships_satisfied = False - failed_reasons.append(f"No non-empty top heading found in column {col_idx}") + current_failed_reasons.append(f"No non-empty top heading found in column {col_idx}") else: top_similarity = fuzz.ratio(self.top_heading, top_heading_cell) / 100.0 if top_similarity < threshold: all_relationships_satisfied = False - failed_reasons.append(f"Top heading '{top_heading_cell}' doesn't match expected '{self.top_heading}' (similarity: {top_similarity:.2f})") + current_failed_reasons.append(f"Top heading '{top_heading_cell}' doesn't match expected '{self.top_heading}' (similarity: {top_similarity:.2f})") # Check left heading relationship if self.left_heading and col_idx > 0: @@ -308,24 +381,26 @@ class TableTest(BasePDFTest): if not left_heading_cell: all_relationships_satisfied = False - failed_reasons.append(f"No non-empty left heading found in row {row_idx}") + current_failed_reasons.append(f"No non-empty left heading found in row {row_idx}") else: left_heading_similarity = fuzz.ratio(self.left_heading, left_heading_cell) / 100.0 if left_heading_similarity < threshold: all_relationships_satisfied = False - failed_reasons.append(f"Left heading '{left_heading_cell}' doesn't match expected '{self.left_heading}' (similarity: {left_heading_similarity:.2f})") + current_failed_reasons.append(f"Left heading '{left_heading_cell}' doesn't match expected '{self.left_heading}' (similarity: {left_heading_similarity:.2f})") - # If all relationships are satisfied for this cell, the test passes if all_relationships_satisfied: return True, "" - + else: + failed_reasons.extend(current_failed_reasons) + # If we've gone through all tables and all matching cells and none satisfied all relationships if not failed_reasons: return False, f"No cell matching '{self.cell}' found in any table with threshold {threshold}" else: return False, f"Found cells matching '{self.cell}' but relationships were not satisfied: {'; '.join(failed_reasons)}" + def load_tests(jsonl_file: str) -> List[BasePDFTest]: """ Load tests from a JSONL file. @@ -376,4 +451,4 @@ def save_tests(tests: List[BasePDFTest], jsonl_file: str) -> None: """ with open(jsonl_file, "w") as file: for test in tests: - file.write(json.dumps(asdict(test)) + "\n") + file.write(json.dumps(asdict(test)) + "\n") \ No newline at end of file From 1545a6d5150c7008c7e24ff863a3d10d9d3d5de4 Mon Sep 17 00:00:00 2001 From: Jake Poznanski Date: Tue, 4 Mar 2025 15:08:59 -0800 Subject: [PATCH 8/8] Adding more work on diffs --- olmocr/bench/benchmark.py | 1 - olmocr/bench/convert.py | 17 ++++-- .../bench/sample_data/chatgpt/earnings_1.md | 33 +++++++++++ olmocr/bench/sample_data/gotocr/earnings_1.md | 55 ++++++++++++++++++ olmocr/bench/sample_data/marker/earnings_1.md | 40 +++++++++++++ olmocr/bench/sample_data/olmocr/earnings_1.md | 32 ++++++++++ olmocr/bench/sample_data/olmocr/earnings_2.md | 33 +++++++++++ olmocr/bench/sample_data/olmocr/earnings_3.md | 33 +++++++++++ olmocr/bench/sample_data/olmocr/earnings_4.md | 29 +++++++++ olmocr/bench/sample_data/olmocr/earnings_5.md | 29 +++++++++ olmocr/bench/sample_data/pdfs/earnings.pdf | Bin 0 -> 109863 bytes olmocr/bench/scripts/convert_all.sh | 18 ++++++ 12 files changed, 314 insertions(+), 6 deletions(-) create mode 100644 olmocr/bench/sample_data/chatgpt/earnings_1.md create mode 100644 olmocr/bench/sample_data/gotocr/earnings_1.md create mode 100644 olmocr/bench/sample_data/marker/earnings_1.md create mode 100644 olmocr/bench/sample_data/olmocr/earnings_1.md create mode 100644 olmocr/bench/sample_data/olmocr/earnings_2.md create mode 100644 olmocr/bench/sample_data/olmocr/earnings_3.md create mode 100644 olmocr/bench/sample_data/olmocr/earnings_4.md create mode 100644 olmocr/bench/sample_data/olmocr/earnings_5.md create mode 100644 olmocr/bench/sample_data/pdfs/earnings.pdf create mode 100644 olmocr/bench/scripts/convert_all.sh diff --git a/olmocr/bench/benchmark.py b/olmocr/bench/benchmark.py index d8b8fa5..3fce2b7 100644 --- a/olmocr/bench/benchmark.py +++ b/olmocr/bench/benchmark.py @@ -183,7 +183,6 @@ def main(): else: status = f"{overall_score * 100:0.1f}%" print(f"{candidate_name:20s} : Average Score: {overall_score * 100:0.1f}% over {total_tests:3d} tests - {status}") - print(" Breakdown by test type:") for ttype, scores in test_type_breakdown.items(): if scores: avg = sum(scores) / len(scores) * 100 diff --git a/olmocr/bench/convert.py b/olmocr/bench/convert.py index 5a94f9d..ba6174a 100644 --- a/olmocr/bench/convert.py +++ b/olmocr/bench/convert.py @@ -40,7 +40,7 @@ def parse_method_arg(method_arg): return name, kwargs, folder_name -async def process_pdfs(config, pdf_directory, data_directory, repeats): +async def process_pdfs(config, pdf_directory, data_directory, repeats, force): """Process PDFs with both sync and async functions""" for candidate in config.keys(): print(f"Starting conversion using {candidate} with kwargs: {config[candidate]['kwargs']}") @@ -59,6 +59,14 @@ async def process_pdfs(config, pdf_directory, data_directory, repeats): base_name = os.path.basename(pdf_path).replace(".pdf", "") for i in range(1, repeats + 1): + output_filename = f"{base_name}_{i}.md" + output_path = os.path.join(candidate_output_dir, output_filename) + + if os.path.exists(output_path) and not force: + print(f"Skipping {base_name}_{i} for {candidate}, file already exists") + print("Rerun with --force flag to force regeneration") + continue + try: if is_async: # Run async function @@ -73,8 +81,6 @@ async def process_pdfs(config, pdf_directory, data_directory, repeats): print(f"Warning, did not get output for {base_name}_{i}") continue - output_filename = f"{base_name}_{i}.md" - output_path = os.path.join(candidate_output_dir, output_filename) with open(output_path, "w") as out_f: out_f.write(markdown) @@ -89,7 +95,8 @@ if __name__ == "__main__": "Use 'name=folder_name' to specify a custom output folder name.", ) parser.add_argument("--repeats", type=int, default=1, help="Number of times to repeat the conversion for each PDF.") - parser.add_argument("--dir", type=str, default=os.path.join(os.path.dirname(__file__), "mining_data"), help="Path to the data folder in which to save outputs, pdfs should be in /pdfs folder within it.") + parser.add_argument("--dir", type=str, default=os.path.join(os.path.dirname(__file__), "sample_data"), help="Path to the data folder in which to save outputs, pdfs should be in /pdfs folder within it.") + parser.add_argument("--force", action="store_true", default=False, help="Force regenerating of output files, even if they already exist") args = parser.parse_args() # Mapping of method names to a tuple: (module path, function name) @@ -117,4 +124,4 @@ if __name__ == "__main__": pdf_directory = os.path.join(data_directory, "pdfs") # Run the async process function - asyncio.run(process_pdfs(config, pdf_directory, data_directory, args.repeats)) + asyncio.run(process_pdfs(config, pdf_directory, data_directory, args.repeats, args.force)) diff --git a/olmocr/bench/sample_data/chatgpt/earnings_1.md b/olmocr/bench/sample_data/chatgpt/earnings_1.md new file mode 100644 index 0000000..e41f830 --- /dev/null +++ b/olmocr/bench/sample_data/chatgpt/earnings_1.md @@ -0,0 +1,33 @@ +Recently Issued Accounting Pronouncements + +Recently Adopted Accounting Pronouncement + +In November 2023, the Financial Accounting Standards Board, or FASB, issued a new accounting standard requiring disclosures of significant expenses in operating segments. We adopted this standard in our fiscal year 2025 annual report. Refer to Note 16 of the Notes to the Consolidated Financial Statements in Part IV, Item 15 of this Annual Report on Form 10-K for further information. + +Recent Accounting Pronouncements Not Yet Adopted + +In December 2023, the FASB issued a new accounting standard which includes new and updated income tax disclosures, including disaggregation of information in the rate reconciliation and income taxes paid. We expect to adopt this standard in our fiscal year 2028 annual report. We do not expect the adoption of this standard to have a material impact on our Consolidated Financial Statements other than additional disclosures. + +In November 2024, the FASB issued a new accounting standard requiring disclosures of certain additional expense information on an annual and interim basis, including, among other items, the amounts of purchases of inventory, employee compensation, depreciation and intangible asset amortization included within each income statement expense caption, as applicable. We expect to adopt this standard in our fiscal year 2028 annual report. We do not expect the adoption of this standard to have a material impact on our Consolidated Financial Statements other than additional disclosures. + +Note 2 - Business Combination + +Termination of the Arm Share Purchase Agreement + +In February 2022, NVIDIA and SoftBank Group Corp., or SoftBank, announced the termination of the Share Purchase Agreement whereby NVIDIA would have acquired Arm from SoftBank. The parties agreed to terminate it due to significant regulatory challenges preventing the completion of the transaction. We recorded an acquisition termination cost of $1.4 billion in fiscal year 2023 reflecting the write-off of the prepayment provided at signing. + +Note 3 - Stock-Based Compensation + +Stock-based compensation expense is associated with RSUs, PSUs, market-based PSUs, and our ESPP. + +Consolidated Statements of Income include stock-based compensation expense, net of amounts capitalized into inventory and subsequently recognized to cost of revenue, as follows: + +| Year Ended | Jan 26, 2025 | Jan 28, 2024 | Jan 29, 2023 | +|---------------------|-------------|-------------|-------------| +| | (In millions) | | | +| Cost of revenue | $178 | $141 | $138 | +| Research and development | $3,423 | $2,532 | $1,892 | +| Sales, general and administrative | $1,136 | $876 | $680 | +| Total | $4,737 | $3,549 | $2,710 | + +Stock-based compensation capitalized in inventories was not significant during fiscal years 2025, 2024, and 2023. \ No newline at end of file diff --git a/olmocr/bench/sample_data/gotocr/earnings_1.md b/olmocr/bench/sample_data/gotocr/earnings_1.md new file mode 100644 index 0000000..8d2f04d --- /dev/null +++ b/olmocr/bench/sample_data/gotocr/earnings_1.md @@ -0,0 +1,55 @@ +Table of Contents +NVIDIA Corporation and Subsidiaries +Notes to the Consolidated Financial Statements +(Continued) +Recently Issued Accounting Pronouncements +Recently Adopted Accounting Pronouncement +In November 2023, the Financial Accounting Standards Board, or FASB, issued a new accounting standard requiring disclosures of significant expenses in +operating segments. We adopted this standard in our fiscal year 2025 annual report. Refer to Note 16 of the Notes to the Consolidated Financial Statements in +the financial statements, and the financial statement of further information. +Recent Accounting Pronouncements Not Yet Adopted +In December 2023, the FASB issued a new accounting standard which includes new and updated income tax disclosures, including disaggregation of +information in the rate reconciliation and income taxes paid. We expect to adopt this standard in our fiscal year 2026 annual report. We do not expect the +adoption of this standard to have a material impact on our Consolidated Financial Statements other than additional disclosures. +In November 2024, the FASB issued a new accounting standard requiring disclosures of certain additional expense information on an annual and interim basis, +including, among other items, the amounts of purchases of inventory, employee compensation, depreciation and intangible asset amortization included within +each income statement expense option, as applicable. We expect to adopt this standard in our fiscal year 2025 annual report. We do not expect the adoption of +the net assets of the Company in connection our Consolidated Financial Statements other than additional disclosures. +Note 2 - Business Combination +Termination of the Arm Share Purchase Agreement +In February 2022, NVIDIA and SoftBank Group Corp, or SoftBank, announced the termination of the Share Purchase Agreement whereby NVIDIA would have +acquired Arm from SoftBank. The parties agreed to terminate it due to significant regulatory challenges preventing the completion of the transaction. We +recorded an acquisition termination cost of $1.4 billion in fiscal year 2023 feeling the write-off of the prepayment provided at signing. +Note 3 - Stock-Based Compensation +Stock-based compensation expense is associated with RSUs, PSUs, market-based PSUs, and our ESPP. +Consolidated Statements of income include stock-based compensation expense, net of amounts capitalized into inventory and subsequently recognized to cost +of revenue, as follows: +Year Ended +Jan 26, 2025 +Jan 28, 2024 +Jan 29, 2023 +(In millions) +Cost of revenue +$ +178 +$ +141 +$ +138 +Research and development +3,423 +2,532 +1,092 +Sales, general and administrative +1,136 +676 +680 +Total +$ +4,737 +$ +3,549 +$ +2,710 +Stock-based compensation capitalized in inventories was not significant during fiscal years 2025, 2024, and 2023. +62 \ No newline at end of file diff --git a/olmocr/bench/sample_data/marker/earnings_1.md b/olmocr/bench/sample_data/marker/earnings_1.md new file mode 100644 index 0000000..a092262 --- /dev/null +++ b/olmocr/bench/sample_data/marker/earnings_1.md @@ -0,0 +1,40 @@ +### **Table of Contents** + +**NVIDIA Corporation and Subsidiaries Notes to the Consolidated Financial Statements** (Continued) + +**Recently Issued Accounting Pronouncements** + +#### **Recently Adopted Accounting Pronouncement** + +In November 2023, the Financial Accounting Standards Board, or FASB, issued a new accounting standard requiring disclosures of significant expenses in operating segments. We adopted this standard in our fiscal year 2025 annual report. Refer to Note 16 of the Notes to the Consolidated Financial Statements in Part IV, Item 15 of this Annual Report on Form 10-K for further information. + +#### **Recent Accounting Pronouncements Not Yet Adopted** + +In December 2023, the FASB issued a new accounting standard which includes new and updated income tax disclosures, including disaggregation of information in the rate reconciliation and income taxes paid. We expect to adopt this standard in our fiscal year 2026 annual report. We do not expect the adoption of this standard to have a material impact on our Consolidated Financial Statements other than additional disclosures. + +In November 2024, the FASB issued a new accounting standard requiring disclosures of certain additional expense information on an annual and interim basis, including, among other items, the amounts of purchases of inventory, employee compensation, depreciation and intangible asset amortization included within each income statement expense caption, as applicable. We expect to adopt this standard in our fiscal year 2028 annual report. We do not expect the adoption of this standard to have a material impact on our Consolidated Financial Statements other than additional disclosures. + +# **Note 2 - Business Combination** + +#### **Termination of the Arm Share Purchase Agreement** + +In February 2022, NVIDIA and SoftBank Group Corp, or SoftBank, announced the termination of the Share Purchase Agreement whereby NVIDIA would have acquired Arm from SoftBank. The parties agreed to terminate it due to significant regulatory challenges preventing the completion of the transaction. We recorded an acquisition termination cost of \$1.4 billion in fiscal year 2023 reflecting the write-off of the prepayment provided at signing. + +# **Note 3 - Stock-Based Compensation** + +Stock-based compensation expense is associated with RSUs, PSUs, market-based PSUs, and our ESPP. + +Consolidated Statements of Income include stock-based compensation expense, net of amounts capitalized into inventory and subsequently recognized to cost of revenue, as follows: + +| | Year Ended | | | | | | +|-----------------------------------|--------------|----|---------------|----|--------------|--| +| | Jan 26, 2025 | | Jan 28, 2024 | | Jan 29, 2023 | | +| | | | (In millions) | | | | +| Cost of revenue | \$
178 | \$ | 141 | \$ | 138 | | +| Research and development | 3,423 | | 2,532 | | 1,892 | | +| Sales, general and administrative | 1,136 | | 876 | | 680 | | +| Total | \$
4,737 | \$ | 3,549 | \$ | 2,710 | | + +Stock-based compensation capitalized in inventories was not significant during fiscal years 2025, 2024, and 2023. + +## 62 \ No newline at end of file diff --git a/olmocr/bench/sample_data/olmocr/earnings_1.md b/olmocr/bench/sample_data/olmocr/earnings_1.md new file mode 100644 index 0000000..ae1deab --- /dev/null +++ b/olmocr/bench/sample_data/olmocr/earnings_1.md @@ -0,0 +1,32 @@ +Recently Issued Accounting Pronouncements + +Recently Adopted Accounting Pronouncement + +In November 2023, the Financial Accounting Standards Board, or FASB, issued a new accounting standard requiring disclosures of significant expenses in operating segments. We adopted this standard in our fiscal year 2025 annual report. Refer to Note 16 of the Notes to the Consolidated Financial Statements in Part IV, Item 15 of this Annual Report on Form 10-K for further information. + +Recent Accounting Pronouncements Not Yet Adopted + +In December 2023, the FASB issued a new accounting standard which includes new and updated income tax disclosures, including disaggregation of information in the rate reconciliation and income taxes paid. We expect to adopt this standard in our fiscal year 2026 annual report. We do not expect the adoption of this standard to have a material impact on our Consolidated Financial Statements other than additional disclosures. + +In November 2024, the FASB issued a new accounting standard requiring disclosures of certain additional expense information on an annual and interim basis, including, among other items, the amounts of purchases of inventory, employee compensation, depreciation and intangible asset amortization included within each income statement expense caption, as applicable. We expect to adopt this standard in our fiscal year 2028 annual report. We do not expect the adoption of this standard to have a material impact on our Consolidated Financial Statements other than additional disclosures. + +Note 2 - Business Combination + +Termination of the Arm Share Purchase Agreement + +In February 2022, NVIDIA and SoftBank Group Corp, or SoftBank, announced the termination of the Share Purchase Agreement whereby NVIDIA would have acquired Arm from SoftBank. The parties agreed to terminate it due to significant regulatory challenges preventing the completion of the transaction. We recorded an acquisition termination cost of $1.4 billion in fiscal year 2023 reflecting the write-off of the prepayment provided at signing. + +Note 3 - Stock-Based Compensation + +Stock-based compensation expense is associated with RSUs, PSUs, market-based PSUs, and our ESPP. + +Consolidated Statements of Income include stock-based compensation expense, net of amounts capitalized into inventory and subsequently recognized to cost of revenue, as follows: + +| | Jan 26, 2025 | Jan 28, 2024 | Jan 29, 2023 | +|---------------------|-------------|-------------|-------------| +| Cost of revenue | $178 | $141 | $138 | +| Research and development | 3,423 | 2,532 | 1,892 | +| Sales, general and administrative | 1,136 | 876 | 680 | +| Total | $4,737 | $3,549 | $2,710 | + +Stock-based compensation capitalized in inventories was not significant during fiscal years 2025, 2024, and 2023. \ No newline at end of file diff --git a/olmocr/bench/sample_data/olmocr/earnings_2.md b/olmocr/bench/sample_data/olmocr/earnings_2.md new file mode 100644 index 0000000..624fff9 --- /dev/null +++ b/olmocr/bench/sample_data/olmocr/earnings_2.md @@ -0,0 +1,33 @@ +Recently Issued Accounting Pronouncements + +Recently Adopted Accounting Pronouncement + +In November 2023, the Financial Accounting Standards Board, or FASB, issued a new accounting standard requiring disclosures of significant expenses in operating segments. We adopted this standard in our fiscal year 2025 annual report. Refer to Note 16 of the Notes to the Consolidated Financial Statements in Part IV, Item 15 of this Annual Report on Form 10-K for further information. + +Recent Accounting Pronouncements Not Yet Adopted + +In December 2023, the FASB issued a new accounting standard which includes new and updated income tax disclosures, including disaggregation of information in the rate reconciliation and income taxes paid. We expect to adopt this standard in our fiscal year 2026 annual report. We do not expect the adoption of this standard to have a material impact on our Consolidated Financial Statements other than additional disclosures. + +In November 2024, the FASB issued a new accounting standard requiring disclosures of certain additional expense information on an annual and interim basis, including, among other items, the amounts of purchases of inventory, employee compensation, depreciation and intangible asset amortization included within each income statement expense caption, as applicable. We expect to adopt this standard in our fiscal year 2028 annual report. We do not expect the adoption of this standard to have a material impact on our Consolidated Financial Statements other than additional disclosures. + +Note 2 - Business Combination + +Termination of the Arm Share Purchase Agreement + +In February 2022, NVIDIA and SoftBank Group Corp, or SoftBank, announced the termination of the Share Purchase Agreement whereby NVIDIA would have acquired Arm from SoftBank. The parties agreed to terminate it due to significant regulatory challenges preventing the completion of the transaction. We recorded an acquisition termination cost of $1.4 billion in fiscal year 2023 reflecting the write-off of the prepayment provided at signing. + +Note 3 - Stock-Based Compensation + +Stock-based compensation expense is associated with RSUs, PSUs, market-based PSUs, and our ESPP. + +Consolidated Statements of Income include stock-based compensation expense, net of amounts capitalized into inventory and subsequently recognized to cost of revenue, as follows: + +| | Year Ended | +|------------------------|------------| +| | Jan 29, 2023 | Jan 28, 2024 | Jan 29, 2023 | +| Cost of revenue | $ 178 | $ 141 | $ 138 | +| Research and development | 3,423 | 2,532 | 1,892 | +| Sales, general and administrative | 1,136 | 876 | 680 | +| Total | $ 4,737 | $ 3,549 | $ 2,710 | + +Stock-based compensation capitalized in inventories was not significant during fiscal years 2025, 2024, and 2023. \ No newline at end of file diff --git a/olmocr/bench/sample_data/olmocr/earnings_3.md b/olmocr/bench/sample_data/olmocr/earnings_3.md new file mode 100644 index 0000000..de43e19 --- /dev/null +++ b/olmocr/bench/sample_data/olmocr/earnings_3.md @@ -0,0 +1,33 @@ +Recently Issued Accounting Pronouncements + +Recently Adopted Accounting Pronouncement + +In November 2023, the Financial Accounting Standards Board, or FASB, issued a new accounting standard requiring disclosures of significant expenses in operating segments. We adopted this standard in our fiscal year 2025 annual report. Refer to Note 16 of the Notes to the Consolidated Financial Statements in Part IV, Item 15 of this Annual Report on Form 10-K for further information. + +Recent Accounting Pronouncements Not Yet Adopted + +In December 2023, the FASB issued a new accounting standard which includes new and updated income tax disclosures, including disaggregation of information in the rate reconciliation and income taxes paid. We expect to adopt this standard in our fiscal year 2026 annual report. We do not expect the adoption of this standard to have a material impact on our Consolidated Financial Statements other than additional disclosures. + +In November 2024, the FASB issued a new accounting standard requiring disclosures of certain additional expense information on an annual and interim basis, including, among other items, the amounts of purchases of inventory, employee compensation, depreciation and intangible asset amortization included within each income statement expense caption, as applicable. We expect to adopt this standard in our fiscal year 2028 annual report. We do not expect the adoption of this standard to have a material impact on our Consolidated Financial Statements other than additional disclosures. + +Note 2 - Business Combination + +Termination of the Arm Share Purchase Agreement + +In February 2022, NVIDIA and SoftBank Group Corp, or SoftBank, announced the termination of the Share Purchase Agreement whereby NVIDIA would have acquired Arm from SoftBank. The parties agreed to terminate it due to significant regulatory challenges preventing the completion of the transaction. We recorded an acquisition termination cost of $1.4 billion in fiscal year 2023 reflecting the write-off of the prepayment provided at signing. + +Note 3 - Stock-Based Compensation + +Stock-based compensation expense is associated with RSUs, PSUs, market-based PSUs, and our ESPP. + +Consolidated Statements of Income include stock-based compensation expense, net of amounts capitalized into inventory and subsequently recognized to cost of revenue, as follows: + +| | Year Ended | +|----------------------|---------------------| +| | Jan 26, 2025 | Jan 28, 2024 | Jan 29, 2023 | +| Cost of revenue | $ 4,737 | $ 3,549 | $ 2,710 | +| Research and development | 3,423 | 2,532 | 1,892 | +| Sales, general and administrative | 1,136 | 876 | 680 | +| Total | $ 9,300 | $ 6,997 | $ 5,282 | + +Stock-based compensation capitalized in inventories was not significant during fiscal years 2025, 2024, and 2023. \ No newline at end of file diff --git a/olmocr/bench/sample_data/olmocr/earnings_4.md b/olmocr/bench/sample_data/olmocr/earnings_4.md new file mode 100644 index 0000000..8ade8cc --- /dev/null +++ b/olmocr/bench/sample_data/olmocr/earnings_4.md @@ -0,0 +1,29 @@ +Recently Issued Accounting Pronouncements + +Recently Adopted Accounting Pronouncement +In November 2023, the Financial Accounting Standards Board, or FASB, issued a new accounting standard requiring disclosures of significant expenses in operating segments. We adopted this standard in our fiscal year 2025 annual report. Refer to Note 16 of the Notes to the Consolidated Financial Statements in Part IV, Item 15 of this Annual Report on Form 10-K for further information. + +Recent Accounting Pronouncements Not Yet Adopted +In December 2023, the FASB issued a new accounting standard which includes new and updated income tax disclosures, including disaggregation of information in the rate reconciliation and income taxes paid. We expect to adopt this standard in our fiscal year 2026 annual report. We do not expect the adoption of this standard to have a material impact on our Consolidated Financial Statements other than additional disclosures. + +In November 2024, the FASB issued a new accounting standard requiring disclosures of certain additional expense information on an annual and interim basis, including, among other items, the amounts of purchases of inventory, employee compensation, depreciation and intangible asset amortization included within each income statement expense caption, as applicable. We expect to adopt this standard in our fiscal year 2028 annual report. We do not expect the adoption of this standard to have a material impact on our Consolidated Financial Statements other than additional disclosures. + +Note 2 - Business Combination + +Termination of the Arm Share Purchase Agreement +In February 2022, NVIDIA and SoftBank Group Corp, or SoftBank, announced the termination of the Share Purchase Agreement whereby NVIDIA would have acquired Arm from SoftBank. The parties agreed to terminate it due to significant regulatory challenges preventing the completion of the transaction. We recorded an acquisition termination cost of $1.4 billion in fiscal year 2023 reflecting the write-off of the prepayment provided at signing. + +Note 3 - Stock-Based Compensation + +Stock-based compensation expense is associated with RSUs, PSUs, market-based PSUs, and our ESPP. + +Consolidated Statements of Income include stock-based compensation expense, net of amounts capitalized into inventory and subsequently recognized to cost of revenue, as follows: + +| | Jan 26, 2025 | Jan 28, 2024 | Jan 29, 2023 | +|----------------|-------------|-------------|-------------| +| Cost of revenue| $3,549 | $3,423 | $2,532 | +| Research and development| $1,892 | $2,710 | $1,136 | +| Sales, general and administrative| $138 | $141 | $178 | +| Total | $4,737 | $4,774 | $3,549 | + +Stock-based compensation capitalized in inventories was not significant during fiscal years 2025, 2024, and 2023. \ No newline at end of file diff --git a/olmocr/bench/sample_data/olmocr/earnings_5.md b/olmocr/bench/sample_data/olmocr/earnings_5.md new file mode 100644 index 0000000..e0d98f4 --- /dev/null +++ b/olmocr/bench/sample_data/olmocr/earnings_5.md @@ -0,0 +1,29 @@ +Recently Issued Accounting Pronouncements + +Recently Adopted Accounting Pronouncement +In November 2023, the Financial Accounting Standards Board, or FASB, issued a new accounting standard requiring disclosures of significant expenses in operating segments. We adopted this standard in our fiscal year 2025 annual report. Refer to Note 16 of the Notes to the Consolidated Financial Statements in Part IV, Item 15 of this Annual Report on Form 10-K for further information. + +Recent Accounting Pronouncements Not Yet Adopted +In December 2023, the FASB issued a new accounting standard which includes new and updated income tax disclosures, including disaggregation of information in the rate reconciliation and income taxes paid. We expect to adopt this standard in our fiscal year 2026 annual report. We do not expect the adoption of this standard to have a material impact on our Consolidated Financial Statements other than additional disclosures. + +In November 2024, the FASB issued a new accounting standard requiring disclosures of certain additional expense information on an annual and interim basis, including, among other items, the amounts of purchases of inventory, employee compensation, depreciation and intangible asset amortization included within each income statement expense caption, as applicable. We expect to adopt this standard in our fiscal year 2028 annual report. We do not expect the adoption of this standard to have a material impact on our Consolidated Financial Statements other than additional disclosures. + +Note 2 - Business Combination + +Termination of the Arm Share Purchase Agreement +In February 2022, NVIDIA and SoftBank Group Corp, or SoftBank, announced the termination of the Share Purchase Agreement whereby NVIDIA would have acquired Arm from SoftBank. The parties agreed to terminate it due to significant regulatory challenges preventing the completion of the transaction. We recorded an acquisition termination cost of $1.4 billion in fiscal year 2023 reflecting the write-off of the prepayment provided at signing. + +Note 3 - Stock-Based Compensation + +Stock-based compensation expense is associated with RSUs, PSUs, market-based PSUs, and our ESPP. + +Consolidated Statements of Income include stock-based compensation expense, net of amounts capitalized into inventory and subsequently recognized to cost of revenue, as follows: + +| Year Ended | Jan 26, 2025 | Jan 28, 2024 | Jan 29, 2023 | +|------------|--------------|--------------|--------------| +| Cost of revenue | $4,737 (In millions) | $3,549 | $2,710 | +| Research and development | 3,423 | 2,532 | 1,892 | +| Sales, general and administrative | 1,136 | 876 | 680 | +| Total | $4,737 | $3,549 | $2,710 | + +Stock-based compensation capitalized in inventories was not significant during fiscal years 2025, 2024, and 2023. \ No newline at end of file diff --git a/olmocr/bench/sample_data/pdfs/earnings.pdf b/olmocr/bench/sample_data/pdfs/earnings.pdf new file mode 100644 index 0000000000000000000000000000000000000000..3ebcf8632a6765ca746a224226374796cb90aa6d GIT binary patch literal 109863 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

)iYeO=KmTkiN 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

rta{Xfu4*1wVKcjm);@7fV^;wlAof7+7{ACL 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�o7xNze&4Gv}vx0^hje4<`snp;gy94!y^<8)-4zv zyb&nZjB<&`3G-O|b%SIm&L<=DOOMn#@f28hMc>14!W)xu{T;F0RqEXsG4=C}z422U)&;LAuMKM3V>Qyac@%A8QvPo`P=NRl><=M_sjZ7VmL z3N-3LNSoB-BYQC(zHRoCF4lbqjNBr|o@##ylrH|bycoVER8D&jqyhQL8$Xk)6zeMf zCwrC~yS?}<$)W-EC>cMCe3mHxKUtsAzm!*YrZ-%bV&8(%TiegN-?o*LVlSkNeXB-q zYgE$Sn?Bo$Dp!7e`d_3EO`~Tcm11TN!uYHaCyFJ^^7uF3fB5MUN;-A@JmIGVVZPfA z-|m%5n_d#A)Y~eNdqzmx#vOfhFug_6fTl|SKN(T-4ZFB61!(FODgLNO7XWhxBup6A8H=YoGNnsCC5hxUlNWElzHDW%S&=z0LMok&S{gKOzf|} zCEmbDP62i@o%!!JI~NDIk4V3mw&3cb9wSY{3zM3~bIdSL44ekp-;QrdZ*#Cjarju0 zaroJNY>t0z!0w~Hrqj9ptk$R=g{^8@;9nZG`gZ?v_P`zRar;7MB2`Io&tRH<<7b)G zvFHu7@$rgzeE1$ssY=nE+_;#q<^H>4;3ex~-6;Mw(>MO3;3J_Yap+yscdEEqa&6<< z^0xpyj0WR$fIkL8qh2mFBt4ede_ach*pm$Ah-zuNk$2 z9?G+cfVCS_^d1yd~9xb z-`xR--#)OXD$wK-b{0j6coiSH%VyC?Fk*W4E#vqh9&m=Q8DN+c*fxGD#q6bo-y zR<8ITh44Sf<^M=}#YNEj4QRHIm1z}k6-)3_FqbziHUCj!3pO7DdIK!#RJfm{Cb&Pa~1ij8m765XaBULr3bV&UDcqxd1z%pxDjrRVy z>} zHW$a%_DZ`LwC$0|9n?{1kMu2Pi#j=?PzN2VzolL%(xp%Dj3 z%?6z2(@TN$zkg;Gs)fMNWTyU!mMEv7n2F!1CA>>vsoy-Wno6(RjhqOGEuLBezuqk_ zhi}yCUVSiVdzIRV815XNTm}0Q4ZlQDZGo>v*F%P$77^ZvAnxEONoTPVJYJcvVxXbn zA!t{x!SP{O)>*K;?>ddxwv?E&0Hc3$6J~5M0HV4b9^l(a z+o>TB#x4JLWI9l?j$CuR9K%-0rHzeQ=xlT!gC;yQM;Bv;9<|oT7HzbLDcYQ% zrbM|A-M^ir-SCpNhBEohKX**4w@H3dF=PO)q4=y0M)t!%hHs1`1qWuN4|-kMc2SM} z#FFriZe(ce#E8U888<)k*s=Z&?ne2JZ=tV_rj}?429JJTER76Y@skQ49pb#|jtpEB z*odxIn&HA(Qf50Ry=)LK`@jL_(9w&lnO?!jzV)IvgxONxWly_jxuq37Ri%&H^=}S` z4(K&KeeFB8!|*#k1FnG5IxeRuDbUI&uUXqSe9`MQcvk+LMwu_+}%f>SzsmO-D>&) z6wUJoMQy(zTrI4siC5zHXAiI)l-8C9#*KVe+_q)DbCXeAr|EE?ZSH;Z5xV|WXc(5U z%r*=&+O+!>4KQFZUpI5YR}q<-5*xEj zh~r{&Xc?=PX?B!1(UwA|DLpl*)IX7n?pP5nH3H1%(Mr3*Co3h52N0O10K)*V-=uLh z_eG-N&ij}gOR|y$9JojFbi$d*)p1x$2m6;j^wl}f$9pLKQmT8{<1IX#KK)9X{94Vq zYJWr8C(oG7?|x15YKAkzI#FeP=K`8Lhkzr^*=%s+{bj#%JoemoP0w1_!G4`fU2`1j z<~ZB#O1goq)C8KHH|oFk)%!f1aU-Ux^Bo@p_%Px+CWVHN3FRr}&NMBLHT3JbxnKN|`;g1yX zjJ`u(?*(`%pd(``;3lDFx6p?ah};KlD@V1Gk=KU9PRaIt}t2a7c#5M=?p+dL@Z@dn@%)< zU$o)$4Gz03dk&qsB6_>@uTCeqHwZzRL1$f)dOt?Z6Vf3%f4O@nb0WJ%?&hy2XYm_T z%F!tQX?g1u;g0zk6>oxRLAdj%EqvzZB!=MBz8izAO<^G7y;0I*QF)D|yv&TGvO>oE zZwZ`{9675qqih?`!+Y)!-UfZd`SGkMfd47H^$WGKx62)@)FJKdWl1ADepGYIH*Wz- zeRMK>YPCvgMQ%9P=_?Jv-7u5-x#2bS8d5J(*B*l%4vinTui(|c2#CDzK;69#Zat-Q ztDkS{Fa`VkVy%OOeWJF=Fk2VQ$n=awFSRVL|I93Cf+pE`Iwz*5{)Qez2QYeuXTa#( z(=cfZw?e+Yk=UGK>^n#0J_fvwLC1U7S>t;8%i$#bfmSr%(f2C#Gq__2xPZwVY&s{c zlr2SRFA+TKEqB7s3W!+TIe?xr$>xpd+w$z!z3TF_tQ_`*ifFuUEsg;HkvzLnYoiT| z_5?S;mgPd(Dug;M86FW1j24d#PjDNGuGnZFuze5xU7wKAultpDQ0Zq5L(m)zuph5Q zlD1B|GpSvR!ZJGp+}H520_L=9k#+x!H9PgJrUru`!yS%vV?af^t4l{Qw!Y5{ zw}ZjdiN4kT}O~afhI!3-7%^-!xrRak($zNk_a5n*^MbajB0t9 zcQF0-KkAscg8tZv0B@fCWLcm>6>9!sBS?o$8wx6ypTw+_L}~5mqjp0IWDW^BVGGzZ z{uB;`-y^JbaY2t?nqiSDe;s$@dfBE?ND4rJH0zkEMuERp!b(x_xT>(ulNzZgonwtv zMCGDINdIf-`VP{D2=b+J+xDsy^X0av?97j7Au2;!W^X~RClauuVsM0*nBsOfDRkWHc3P$0l5SP zDrV&5GqMX}65bU`!2@QG7hYB4kC)#E^znCt)f^w`CANp+U%~+baxx-=kwPSW*{ol+ zHIi1+gd(aXjH$JgmazirSa@28C@18wI$lmXu1Djua4BUvH(VLdN5ck9DWnyD)KqKj z@31|k;IFzHnE1G>+sr(E!zB#&ysAma#&&%u|J@_2qumI*7C3*>jkXVgiM`7=n3*Mu zuM@#4Hx!EupU9#&#XQFmM`gEPm||BXORSE>wQoq*$Ud-+Jcz@1-|TkxED*^qh}H>? z+8RYAx{W9~SjNq>WOHor#SZj0dO4ieK5Zh(w%x1l>#18-avFoHpiO%(6pIoU)>o|W zr_goM^YZ3DXHgS7UQO`!7C^^WPIb zTm25m6Z%1z&HKizjpO<8xyDImjmQeKS&p#Br5~R^o61<6+}>SP-}Fy zB;;ZD)ri*oXL-@G=(B#8tREk}9P?bcSE`xmgvC$$T({ytw!+XuJSm3css%9B{;9f%e z^nVkc~?&UOlI?zJYc=dsEJ-h*BBdg z*h&A~i|>kAYE#h)Ygyf8*E;83k#NHjykQ$#;@ibEsY^fY({g^5_cK=O#r>}ZV`J~<*PXnc4fd0Hd-=1v zzO9r#u1iZ2j(Y;~Vxb=wP?Ljh3ljn!qDESrHEvN0Je>M*&i)?@kt*2vaw`mViPB{7 zyR4I}J|ox>#@_(cWiXZm5;7#K}I8AqX ze7F4I6s}^!L-Y36F%fji-@~BT>|xw zv-w`X74PLnZxtXQTiPgf)+1WK%|8N@X0U7>d8K~3OP=@~-j|q8Os-9E&wV$p ze?({66{5hF`Mb5cD>;xvP6Q*Np=z;=jLZpW!(KvqUb(2n2SOiC-`}c`S5V4lhmE+S zspIn2k&W)ZNE*oftM7oJIm>&5S9x;!+-i{O#f!Uaj2SrV7~>IfXa6+8C4~CrIVFO> z0Y)@3Vsr2Zg`uhFe288w6Ak@vj|sZ42n`){$Fik@6q5EA4clha!#01&iWHyQ*OHqQ z)xxH4Z#xx+x))1zde_!Sbg8Sp#wp+M4<6Q-@(;qxDsuKf^=t?~fj8akEi&#*Ex9*- zWq^?N*4mFPfcZXsLyKHZ`pU~a#cWyg$#Pi)}+)*j$|WS zY$w}9fy_OQkAJTlH!&m&zztQ}DWqhvKCra`6mGb)OcyFXX*sIOJR!vR3ZIXp%8I0j zRjyJZh?c;HFjr2Ij3G#jio6NQ+;R_hva6Folj2NJm-jF_azd#7@Ej-gCdOO;TIZSb z;WhSHUzH$*>BNxE+)RIFtIcBmc&*#vipBR(uz|Ra`+Ssg@qzMuJ~=Ywkg{-|ZD%sN zDbtm>LSXXPr>T}HCJwnLQHZV_^}QTL3QFZ^&!+5eVrbOrF^o0r&x~A16?oMqvBTeq z*8ZEg*m*_Mp--a?rZwlr9>5TE<*-X$Q9(werJ_U&Rvg)adC7cPTQnAph8ZgJMhpc? z+!kEwgF&JkGHm>Kqut1W*a1hZD-Ig<&!|{BY&ySd3qjAqGNinDspN1lczn%fUaR$| zvTD=xx)h%`Vrg#-%qpdaB*uSi^oj8}D;Tf!HWenCLh;Axw62O$h=Re%u~h-PbvgUX zZ=EqEeA#K$Rgc>MU$1I0kiV@N@U3&YWTFfs=0id9;a9L@mf4w@E5QA0Yxw}KN!Iia z+=W2ISpLtgr&t+(80Ay7P#w9pobvUM$S9y@4NhTzMM=L2^kf@D9ZqUrApCEgNCqe$ z&71``RIajJ?8GZhnQRlm*D2@0{5wP{dL{zBECR&+a%7=$v zmKmgoLWfo?w5O;W%^YUAkRXsd92%P+^ROrIgL&akiMGQLZ3}vo`n>00y5>REilw#OX_z3xoAh3sE$#E^xEp}PfQO&Uy=W{C}jhD{K(*VMeL3&HyFC`Q(* ztHj6vb*#!zziv2TZ+18(H*T|eSymW_U0J(b5B4i9^Q#8-RD0^2C44cO79wWKK2@^R z(^EZB27VpTX32z_Wy3l*D8o*UWU(HhlN&zAMGI=ju5r7Tj%moG7iKKEG%-c*JUR7m zRN1_)K;+aZ{0X<@u7DhRV%nH0RZ=ZKX?hXYPyNec*>6{#VJgiBFAh-7sn?P?6*QN> zTy?Rqm+uq~52lYU)(h}I1idEWW7TH=W*hPSe&P$?B)U0GqErNC4xX%psm*QY&0pp8G z9eQ3Iy!@6Cvcw#5IagqLxyiZ!8s6!bDE_1{r+Y)EI7AC9{}@mF4NkXrRG|`{yHk8a zw&3^I`RE|;Yfq;`=I!QVf|VaK+x8`<2d^X$P#fw4uvVlOu@NhsHzdFL#LTwi`-l@^03PQBpm|Tnj2^YVcISM z7G=)Z(_z=d~4dk?PFJ&cKC^C zAlIlRc2E06aE!9upE9z@l`N_A+e9l*( zZ-PF{)JqLpm7(?6@(K$IJvaWl;tr&m=LqglEzi6A;8)f2tvm_r#UaKFl+{-i$O6Yd z^V+eD9NYNOzoLOf_2Om@croPnkUBAxTx@Rfr$Nmf6YF4C_TZANb?PR3K&gTT)5Xgz z4eU{A)4?>nlo?UEhCAh)p+GL9?1!V9)XGi*4yqW&R{&k)B|%s978=SX#Ci|>Wg-me zIbS59MF(koT-k-7#^w%dz(z*4Ns@IagfI!%yAk;J1amHz?vR`g$E$$pla(JO1R7UJ zT=)I3zM9!?qc_{$+(H|C_bE!0{O6<2YoNv+xx1Sf18iP;-pBoM(~GF3_e-TbG5)t* zTPZU!QA{hZY>4*F5mgb&h{sUry5ZmJob$2CzfZH3zA^N)tIgsA-SMWx+#yr9+~lFV z_r`8Il*=i@Cv*{p;2H%jwH&e|JNU^;@h+$DaXG+b`Y%!PUQT$XPX*id`TS8Jl_)T0 zeHkXaflkp;@vWo@x~H_cFxMGO<1p-4X4r&9;eY*Hnml;c5)@)!u!Q|thmofxA06yT>E)%1)!F55ff`^UnQh*wjD|jO zBu>PRk7(#3cw3V{&dR&v&aLi!19MgG1ZGJVNk^9R199&4$p>@oe zmDw*rxO2h8I(HMi9ICN?uIP$Yf9xG3Cf0M@o6#e;gRl-z2?^IAGc*Y%UDO!^Ke!}n;w z0tpW+0+)}|F)P)vA1NjN2=gG^mEuhE-1pan+1yG%Dl8%Mxxt-hv~{TbkM}HY0TK&(&{y02{4ek^m@%3z;?!K3GKTuHFBND zj#6`qC!r5ASQS^JsR~6zlAkB(cx#s}Ouz&8YQm0oDcK#kk<3x%BxoFU62_oMV4$;A zO?V$YQ;pie90NIMW3L00BL9?m$;I1yqoM3^`R{B;V zEbyxraZINRC}-gGi}#S~hY$_(l3^BIt-%!Z9iww1p_pUNi?hk!#>_|65ZU0#F63n{ zu8r?Jh$PQr^OQrp_NT`0$^4Q}J_irBUMI(250NN|tTly}M;{qR*vJ{O;IUev@AnW> zUx;>2j(?SFVefX5Q8*@G1+6&4)!jcr5es;>2QLvTxbsnr6-JCrGg2qa^2x0YQC4_D z&yQ<{|2FZPN}mbt?U$6uPJ#Q$mMahgQ3NzDvlXsnq6j;b9p8@&LuyCOjFS!$m-X-A z+=lhOrXlkl z*7@7_oqVReSuFqJ6Vhcm#B-^63kOYCNyn7?;kCmiS`ypuf)~B<>DOe9HM-VLwbazX znQ%@Tq)a$ScV;#GQKwU&X&U8ASdw0~FXz$+r|%`|w!e+X?~m|{2>n=0KT)|ySKl6DaLBE0EDSgxFxZSW|vc^!r)^&4Vh zy*h$dIp>67EvGZB;ZB7%-yCD`qmptb2R%4^ZJ~_B3}@Cvz?bm*E#)lA{t9zQ!x~MT zn=#v8Iun0TVj;dj$O_LRh9S`Xb)x-@{;(hQ*f&h;OMq{$K3(NJjX)}UN5YhneypH$ zmbhQK=j9ieZTI_}2KHD=teBVSkk9I$r#`4g2 zJXE%q)`#ty zm~{gtBctYpfo3T>X`&z&ATW8><-PK!PQtuw2$3r3S!wDlb7wOf+oVB7lYO%&LrY#4 zLjgVR=$)JL3MdTQeqKIDmfFFAXewix!hcNGjdsfmBIXJG1m~-+h}tl}kblpr6X%y0 z+4*tYaXdD$?SRVXse{~DCLNtlM5u?6e|*)+1V^Q8zuBl5cJ;FR3&?y1^J!M;@wG`vhk>7eOoxk!5077}?2u|Z52@_F^ENagD9iph{-PaO)X)RkT+N0iy$)%V#M8hrYw3Dk_*?8d zTuTU(bpewlWmFPRIHdr%l6rzpj_S^)d<$@e$VL%U2Pbi#km(m`GPh^bnj@`5t>Q!D?JL zDwg(uwA{Ng_x<`Yi2IG$_Yiib+x3DFQ(a02&Npu+kDNjL1TU*HkX`luPgH1GSXak*Yz0y;+#mu@{aTL_U(nB;MUq~^8x#?jU z<#GF46kbRq~1R#YAsPH*rRJzyKwuRi60eON|kOwW{b`7-cP zX|l|msbF#)&}k||?uw4bA9pXIvzsRMn4Y<556#1rc3ysZGjf8S_EI+A_2RG^pMetH zwKsdJ*WO!9j5}`_=Px2naJ=sFb*?3AZNHruDK7)49B?GyyBK(c;KGG32174Uo8-SV zh2l`y=Lhcm{2~NzjbTDCbBtk*{uc4Yi^IQf1Fhh&C<5&qN)3zBXU%?8izCH?^@38P;oGWWkp2VnFMg)?by2OH#!GQ?bV5o&bbu&h_^ z*Vd+_59EN_m`pKdDY+N%hvI|icC*=qDIEdI?Uv$Ep5W@6Ix-8bcX7av(gY7)T0xb_ zQv`W(bu@OEFt;H5!B0oq4Ea(1LJ0H1yy`6QbU%v}6+37D;$sqCg!?R~fwq*UD3<&f zqbE>MNhv9?cM{^`okXc7MN9Bd6VOhe@;DNqA04I-yi&zP9JMmhw*5f45g|)3Z z_gwl^tgT*5;w3lw34m;=A79S1V75W-F{B;+E%f`& zs)JHy1TlKkpsw(Q38=LCWqmUsLY7XU&J%wEuqDS!Q{jlz7Xr1@e-JaQlZEL_9?{TX zJ63$Q(oz$7EW($42b;iIDK}TGz#Sst7A1UixmA+j9##N2a`yF+htCc zLkmfHL~+DP)yNg)4}(pbzW=RYg zfsn)x(+6`HBErSg=ddcQApq+4T-}9k(f6Mhj!*xp2h-AV0!m|)*=sqEgCN|?gB*B& zYo!SQNEg1S;P;DNu3Lo4=#3V(hlCfOH_ePd469LoBl@WDh~!|$q33&X<~nH0ch8f( z@R}~ryeMkiJ}EwLXoK)kH@ke+t{^Oit(G;t@h5?e1$O(_Ucb$%YNjz4e?vAcpZwg7cFDkUhzPn zgHl!!bl~O|*wFV~N-!BfBeA3-qtQK>AxvZBTa|MYl7Lo0A44`G!5sfsXxjRO5P=m-`4)vw;hH7KcY}q5*n@m*oGc>vXd%(I1Skb;`<0u0 z-Rv2Pwdu&f;38#KR2O?8Yl!aWpDNf_wTuC_2Iqr^cJ31w<81zC34 z*EBXGwg0mwFN?8NDvKXyFK9&z@v%=hrmwz1j9K(DYLc^E#d`jayLnxfmfx0(geQj7EotAUTmV82t^+wIMPu4no^|K+Rf&ZD}e zknVb$$DO47oO0tet9&9f##XoCqx7CT22%^r*$M4Xa>#Q0M}9^PkS#qrwdiL9Xc-ctyyvuBLbZ@6!y6ni^_s#U8 zOQ7SFYs6W2cYg_6Dum8^JBHlV`DcjnCI=Jt<6(e~Yl4x(F&5vp3qdriWp>DtWNMd# zPMO}%8L0TbTMgd>#@0xq6NdF4#%%bardU>ST4-d6D(S*vNIRo6=)=fF{+J$d{xDu9 zg!B5Q78*0wXj6X{P;Gs0Wh`LbqZ#Jv)@1%JTq9!daT6-iJg312SZ=o}pj zD{Z2ZUUsMt;;fiUgPxvORsrijwGNJN_7*nQUZ^aFqL^O2E8dz7SS{P== zg5@4K8R?GULt-!)zBna4p@Z*im)41oh2kbdR~Y9V9hgHZRMapL7B(~ZM-@2>jK3+G z6P1yD(0;g#yMbVKA7(6WLc|==R$IW2f}81#3< zn^Bblt(Wb~n&(mt6n9bd7>v2HJi55`zP7H~^sI-)%}8t^CSHY#o->gDO+2#~m#1R| zkCCXkBGb@f$Xd|`KU;*eI?8O}E*nZa(>nG7QAau`1^}Dlq<9N&Jk#2*isO6T+56tz z)MaTG`e|X*+S9Z}6XthZhZrdSNH96nGl1HBKnC zud@~9E-$w=u)2tEjpX41d=>?mFFf~-Xt#!PvgYpY{cUWf8w0u_F_b2%#t07UupJ?( zx{alNMaTYPIF`3LBc5gn^N1us4KOcT)@fAcj?^vUHBF+Bgl~cePG(PM*IGaY>h0s% zMc8$M2JIPA_`eSX3C-HZ;>TKOLel$Ue*g75iJTes8~Hy-U3tZ+lR5AP>T4*ns=BJjzg9RhcQ^x> z$esy#^^67+X*)np97L;^aEH{FWj7pW-qcqo#q6(xL9TGUAE=~RjmpVD*Og8p~!CBF2t11fQJuI zrK7%(ms^>%Wau4V64u7HTx#7od}sPXXl5`eh>?7QNq366PmonI zR_dRvC?6OWn17T@aj)Lwad|-4`o?XWQ`A{)-e$kUajL7k$3X8^d{6H`d1p|WS; z(8q{tD<~ySd4bp`1%}QX@p8#(s|gvyvPUvJ>1|$s0M?HWB)=^3hh`r&IBWQB@bU4%aqAD!dn!j&LCUdo9>`sYwP6=h{go;QB+-oH9NA;m~WuO3_jMCY%Oq>h;iGNE->InQK<8iMYha9 z?dHG=l}g0L3sY%hvYN3M9 z2+ltFm*Y-nSNX9sm&4waSD|rckRL)k#nwo>-W+v7L#sYG_62ga)aEkvGPe=(eT|Za z@wcOD$J~G*0hi zfO=UH>+5o5$F}s$9rdMAd+FW<6NqPvBMz(RZ-flPQq4U|2Da)KV1BK=n?|JAHjM(R zLeClptkj=#DWcLGh`wfM6N?-&o?K)h=c*oY8(WhpW@1n-$Wnhu*h}jTL?-5==oun4 ziX*cEq3d1@Q-r}RX|>HDc=%XweC&)}GAfgiEUGk59NEnxI*r00Ryr0=T=Q1+gy1Y= zIRNcgX5wHH&6YOKj+zF}7X!rkXRH!okDcqZk=s_++~IU@$(Flji~%lpp7B8>;aE)A zaf3Vivkz6nBHvCR&GLrp1B{IG=+&2kZIQ7{atA7xjQr zv8pms#-_dx4p6VQHJyjWYfgQB66;NstVC?u0}7y7;wM_bX>O{~Jhon1g_W~MR^E9V zO#SI={&c`GGi2@sQXTFP{>alzN38bE>D7I;*tiZ;`IXXMG83=l@>`&L!5cT&QqwmV zfwi@|G;a?Dx7Nz4dxDyP{DE%7nf*a}MCz!BbooR+npjw@Wo4WzphBrqafipmCz>{* z%JoScX$H>>H|@Kn!hMD*zB9prf~FAJFb_~StqkxZ77?dPP0hJX#`qz90^95-^NhtI zYe$0*O>b?C4W24;<)hXUv)7J10zx;fqoK{S?u3hF1f|sGJN z(t8n?IuUVq`E;osp_q-@p#uul;Q*5*Pr2ZZ6hn|nDWd0RG&4>l8x471YSKe1AEaK< zB3ogD4PA-G#C4x%jjZs6oXfZP+p-)nb791FP~piToeGsx=8>I`;Yy`WUO-Q3(NTT} z@(41*fj+RaFnx%pm#Zf0%=m06O`k>J8)5#0=zNOLndKtAbz>Ii51hX(v4UUlJG@{| z?=t6qa7UuBAXScZ`OIE@PpSk(vS8`rq*>$i;#r9iD(PC{b|I?h$1-FQGWhD(k|nN_ zHLc>~C7}y5_+60ZRe24X@~VmfmxIsziZftY!S~e#$qG6&Y^~8yk+Ma%1^h zY~-up$NsT4{=3Xw+#jFbD1pd%r=*U+POSKzM0*za?{qfwvI7Z-m*WXCZjJkg%?e~K zkVQ4^DVAB=voJg}-Y&c~w%EH%gNu^)dyK)Vugw#Y!$voavD0`)x2S8i-|JXbs}Hv5 z;xA}0;_IYDZTp=uk!FW`;)m~dAqSRtFzC1q3k&H}TeFqAVs&nx7;ue5F{_jg`(cf| zN#iYO(6KY~FuxF#cNntvY51mxtx%gXV9tlTLboO0kY^i{48H0oi2F(loWAEE^g) zjK~=`_uQ+> z)Kp_#vpWY)rMe=sV(K+C_i}H|qG^DMgrv;7&^6L}e>eGPP`kUza{`6HC8yKzg;GKR5jVfD2~=;m^YTR5a5;|Ze0eAGr$w7coK0yDC3C@H?wC1RGdg# zSa($w4C;*j^ppKcU*gP?>V;v?If@}~o&4NnTl+{*|8%^O;=7Ne_SlolcKun`d`xE- zqP5xtYZ?pQ;h=)SBn^u?0{)!Dav4eV`?MDd0_61J(>SyLr4; zg@9&p$ng*oXxDAOuSv9Ak9Bwq^P_bvE0LB|b>{Gq-;v4@seLzAr?f;{CDL&=Km#v& z4MO89FUV<#cLR%8)OOvXX9Fp%Q%DwJb~t}TwUAol(cGnZpMkbx36f{Qi-~6FZS;m7FhR^fR%YbA39{JVI#HnVOQ=SPWE97{QE7bgY{hC<(Am8X3Snw0mh$0bi8 z3VWAKyd5RZGHxo3Yx1E)gbsfMGt~ux2CH-Uu0;X|{IOE=v zMnUZf-Kwj4)@8yr`?f<5A4{RuDX~3$oS@?UEU-3Gk$vWiof4!Th=-|Bm|ORA`V}o( zwI}l4LZV`8aNWDYS<11t%vJ-t@t@Oc&Heo<22OWl!WQnkKNc1jQ`=j|mdG3q>U9Jz z6Bdj2BdR%}KviwMovG)4b^7etah=;IgygGPWAG8!1b!Opz;c za+Q@wyv`|&>XJLMYAHhQE9$51IpN22D|(Ms(l~9YYFSj<&>!@i$eTqF^(r}CM^K2Y zsXCnn(Z(jxY1I{yI^KFmt&#Kz{e2f)V4P=JQSTk|$PtqGjZxOpk~n`hG4a{SAw?9- zbbru}b0?}Yv#}u!0qR$Xh3WY`!VabFnd}=RdN?=OAO*ONL+=z9#)ie~Z>k7((V)HF z?ROCXjriDiWDBGk4BKV29&-O2wC5S~4afiZxb-~3_kkK=4oEQge8ABGk2+2JvN*N9 zdFpYeBmF=&-hNmXZLZ8taTIU<=q^d%zS&`AZroD1Oa*U>p+S5!k9&@v-@ZtvWJbSH znJ)vx|0E{PtI@vmyg|lyKj>Cj_%8pXF>XC(rGAIe-N(+Fv+{N~)se#v#oJ)QVk*Tr z1umnQ3##I6{CdTBZxhMD==1Gl$qAG!`|w$cUJnmf&w{BS@4O2@@{}&mDuZJL*UYZx zOvZ*;C43*0a>>SCUz?JmYB@d`oNB^gvSx4@xlz!pQVmYH&eO9WJDaO z=DB-fF&s?fM830i8vd}o=a~wN#|;c)J#Mr-C+pS;FKd3DKpZHOGx6?@{nDpSHiy6W zID%?_)h%&%98g&~7ndaxCUWVaI_!1J%{%~5J~?-ra*tdM_E-SvUkVL8EQknxeQ^=&6&J_Jt2h-f9=J%`%Xin`xc7v;rM`CkadQTaBXuI zX|iQf`Y3NaSJ@y7`KGFYeTfW6j#{6g8zpQ}oGTJKQyq-D9{J8D96hE~HfwT_jyO@Z z4(1Z2h<{QjtEbviJ$RLd429lAPt#IHZ)ol|SGG+@LPl$lCR&P}}|B&N*cy`)J)sdJFhKnfvH!M1-YNEjHyU<_`AxWU%V_JrIx58mzab zbrH!r;9gH*FQt2|1;Sd)vX&lJ+`Qju%!uD9{ZdM2uiB%#wcL;2PrYTJUBDK(m~qNx zkF`i?xjxokJ2Zp-I#AS78#cpf0kp`YNevChf+Zqx(q2s=O z=c(gC8r-2My!XKxQmFA7uiD>uvO?y{&mK&a9y&W`zo@Mo>H-8!j(4yfKyP%t*CX%` zeXD*-FPxs;U+RVB;Dut7xc+BRJuATT&dXftEucjm<_nyXvi!d)I#&QnkM5 z0)M4^w0zaO+)OwSoG44Q7}=?G$<(2k+Brz$M6?`cgSS@8RYhl9SqVi~=f7Dnz0PRI z_Z=_$9^doizEDtj>wqSQ0l4htW`Qp7%4uSO^+@pme3^mP7ZefHFNUvj%<##Orr|C6 z8Tm9l!FK!$TGMxC45I*4RsIF?yYBb#)ha1F>YhrzxrO>Cmw$JGY z=)3bp|Fz4Ya`ob5x^Cx!gF+;}baWhG62N?2_=?>|Ms@`tX>l+CPap-o#`S!&+fz6Z zmcKAXe#C(56qdt89hun~oG5(u@$sN;w#3bobzE<49&%GyInb1!a|62jxwt4s?A06E zo`VAS1;YRw0*srz3q`0!l@fX!Qg>>7?QyHcj{PHu#QuG^iL) zDC}FWBikluSPVpQg>wjc*e2zQ)S+WOvW}fkTciqyx^~2DZy--Y@ubyjJbR3205qZ~ z|28sD;(;N#L8@;iV~N}(bf>^XW?I&b{(~o-B%O@RqZn$-z@f-sSW*%jwIsC%qcY`c z6TI+DS}=mB$DBWxz&HiW^8Ath727PV0{FR{e8QX(Va;DuRY(xD_RLM&wYNAoBb;Jwy&tyyy3F!;({XHR$sYdlUpGyi$7M=yZsgA32 zDPl$VJVmQ*{Lvp?$oyWV5>)337jqJR30w`)jgQ2O8*xf>R~meOQ!$j?TqSyFLiWQ1 z^ZqL~B_qb)GTnpc4^M>a4latL&4NA&-iH<&l^9?|Kkfx8N;Ddi=WmonfaNB+V%Tir zzZaCguZ*)d(Z{Vm2r8vZ4lX5;8+?#G=J6{8MQeYQP+Bt!!H0uq%2iXfM4vo9#0{FZ zvx zkf34?6zYl3;A`Iv0O^FR_dfWEKkZXSxzAOZ!jE7@I>pBnxpBNJiL-M zTqP-9Cnl&dm;JJNtEn<^teYBzGA`4FM7L7S0zlhW^JGN45hZ1qP5Yhsko0hmNeCj$ zZyq3mp_-CPN(R_bl>J{xG`V7Q?UblH2f(KjZ+=3Q84}?ZQ=SWgh<8HHNkcUuY8EHB zo+eHLXpp!xU>_21ni;owy3VvdMe7@gatJJE+lk)pKgE^nhJ^m;=$MnMDyv3qh~8tg z`mIdDbaGdI!U!QIUn5%N$ti)C^OFj3`$s8}SC6<~#s=9Bz0E$@&aP%EGu=HLThWr4 z2yyiKF%lDlebw+*>aTU`kFH;eVst+9)%H_A-0#U)U?nKBNkEkEVVt&|}$tt9de(QP`w3v_I_AC7gC>2 zc{56^`zld}c9P}lb(v@L&?%McHI1z_SPRSX-Y^rn^wG4)IxAHcx^$0P_vwR|({C{` zK8R!)PU~9;Yd1C0v;Qa9k{hb@{#Mx$5n?kt-48EgHR&IJb90rS_in`_A)d9V(5#rr z*FH3__e##!D2uJN5q;O#II$dMU!(dW)WfnY?~2Ig=FLNXKnJUUKyF+1#ugp*U)wff zs}R*jl2LqFYk?kBRw&`w#&Diy+6#$x)(SK`N4%b2RY4MJQZf3a7aXeDK4((DkvP1a z<4n4$UjH1hnSDr6xWcV{fFXeQ(m@0I4GdWn+?0v`U#vH9MT5@{VEB(?3T8#XIE}Bzddx5)1W};LG9NffhtY zr>DQpDK^eYIC(`^=!w85ba~rD6w&Ag`k$Ga?xiU4PLX-;s?v(grP=YMJbxyELED)0 ztH`Yq|M&DfDPi<~v0=9i0an!+eSN3X8n|+9Qlt%gMGxe#u$-^T&0a(4TO`u!v`)#d z_q&XOK|~j9zXhhT*xpy4l^9n4;tBFOJdcE+a4YE+u$&Mwvt_EaD+ z2R8?ZN}oyml{L%^0ScGo1?n?Xsk}0FcJ-oqAt^0^K$@F-xH4mMioJ5RLa@Y{(nO-=XyIGsN|3`$FKoWtOr(%-i=K^wpf!shI1WTNk z3(*_{$cXqLlqx!y{lAC&4QlVM)OO!Z9w literal 0 HcmV?d00001 diff --git a/olmocr/bench/scripts/convert_all.sh b/olmocr/bench/scripts/convert_all.sh new file mode 100644 index 0000000..13bbb8c --- /dev/null +++ b/olmocr/bench/scripts/convert_all.sh @@ -0,0 +1,18 @@ +#!/bin/bash + +set -e + +# Assuming olmocr env already exists +source activate olmocr +python -m olmocr.bench.convert olmocr --repeats 5 + +pip install marker-pdf +python -m olmocr.bench.convert marker + +pip install verovio +python -m olmocr.bench.convert gotocr + +python -m olmocr.bench.convert chatgpt + + +#python -m olmocr.bench.convert mineru \ No newline at end of file