Auto-backup: 203 scripts (@ 2026-08-28 13:49)
This commit is contained in:
@@ -1,184 +1,382 @@
|
||||
"""Tests for extract_schema.py."""
|
||||
% partial rewrite of the LaTeX2e package for submissions to the
|
||||
% Conference on Neural Information Processing Systems (NeurIPS):
|
||||
%
|
||||
% - uses more LaTeX conventions
|
||||
% - line numbers at submission time replaced with aligned numbers from
|
||||
% lineno package
|
||||
% - \nipsfinalcopy replaced with [final] package option
|
||||
% - automatically loads times package for authors
|
||||
% - loads natbib automatically; this can be suppressed with the
|
||||
% [nonatbib] package option
|
||||
% - adds foot line to first page identifying the conference
|
||||
% - adds preprint option for submission to e.g. arXiv
|
||||
% - conference acronym modified
|
||||
%
|
||||
% Roman Garnett (garnett@wustl.edu) and the many authors of
|
||||
% nips15submit_e.sty, including MK and drstrip@sandia
|
||||
%
|
||||
% last revision: April 2025
|
||||
|
||||
from __future__ import annotations
|
||||
\NeedsTeXFormat{LaTeX2e}
|
||||
\ProvidesPackage{neurips_2025}[2025/04/02 NeurIPS 2025 submission/camera-ready style file]
|
||||
|
||||
% declare final option, which creates camera-ready copy
|
||||
\newif\if@neuripsfinal\@neuripsfinalfalse
|
||||
\DeclareOption{final}{
|
||||
\@neuripsfinaltrue
|
||||
}
|
||||
|
||||
% declare nonatbib option, which does not load natbib in case of
|
||||
% package clash (users can pass options to natbib via
|
||||
% \PassOptionsToPackage)
|
||||
\newif\if@natbib\@natbibtrue
|
||||
\DeclareOption{nonatbib}{
|
||||
\@natbibfalse
|
||||
}
|
||||
|
||||
% declare preprint option, which creates a preprint version ready for
|
||||
% upload to, e.g., arXiv
|
||||
\newif\if@preprint\@preprintfalse
|
||||
\DeclareOption{preprint}{
|
||||
\@preprinttrue
|
||||
}
|
||||
|
||||
\ProcessOptions\relax
|
||||
|
||||
% determine whether this is an anonymized submission
|
||||
\newif\if@submission\@submissiontrue
|
||||
\if@neuripsfinal\@submissionfalse\fi
|
||||
\if@preprint\@submissionfalse\fi
|
||||
|
||||
% fonts
|
||||
\renewcommand{\rmdefault}{ptm}
|
||||
\renewcommand{\sfdefault}{phv}
|
||||
|
||||
% change this every year for notice string at bottom
|
||||
\newcommand{\@neuripsordinal}{39th}
|
||||
\newcommand{\@neuripsyear}{2025}
|
||||
\newcommand{\@neuripslocation}{San Diego}
|
||||
|
||||
% acknowledgments
|
||||
\usepackage{environ}
|
||||
\newcommand{\acksection}{\section*{Acknowledgments and Disclosure of Funding}}
|
||||
\NewEnviron{ack}{%
|
||||
\acksection
|
||||
\BODY
|
||||
}
|
||||
|
||||
|
||||
from extract_schema import (
|
||||
extract_schema,
|
||||
find_negative_prompt_node,
|
||||
find_positive_prompt_node,
|
||||
trace_to_node,
|
||||
)
|
||||
% load natbib unless told otherwise
|
||||
\if@natbib
|
||||
\RequirePackage{natbib}
|
||||
\fi
|
||||
|
||||
% set page geometry
|
||||
\usepackage[verbose=true,letterpaper]{geometry}
|
||||
\AtBeginDocument{
|
||||
\newgeometry{
|
||||
textheight=9in,
|
||||
textwidth=5.5in,
|
||||
top=1in,
|
||||
headheight=12pt,
|
||||
headsep=25pt,
|
||||
footskip=30pt
|
||||
}
|
||||
\@ifpackageloaded{fullpage}
|
||||
{\PackageWarning{neurips_2025}{fullpage package not allowed! Overwriting formatting.}}
|
||||
{}
|
||||
}
|
||||
|
||||
\widowpenalty=10000
|
||||
\clubpenalty=10000
|
||||
\flushbottom
|
||||
\sloppy
|
||||
|
||||
|
||||
# =============================================================================
|
||||
# Connection tracing
|
||||
# =============================================================================
|
||||
% font sizes with reduced leading
|
||||
\renewcommand{\normalsize}{%
|
||||
\@setfontsize\normalsize\@xpt\@xipt
|
||||
\abovedisplayskip 7\p@ \@plus 2\p@ \@minus 5\p@
|
||||
\abovedisplayshortskip \z@ \@plus 3\p@
|
||||
\belowdisplayskip \abovedisplayskip
|
||||
\belowdisplayshortskip 4\p@ \@plus 3\p@ \@minus 3\p@
|
||||
}
|
||||
\normalsize
|
||||
\renewcommand{\small}{%
|
||||
\@setfontsize\small\@ixpt\@xpt
|
||||
\abovedisplayskip 6\p@ \@plus 1.5\p@ \@minus 4\p@
|
||||
\abovedisplayshortskip \z@ \@plus 2\p@
|
||||
\belowdisplayskip \abovedisplayskip
|
||||
\belowdisplayshortskip 3\p@ \@plus 2\p@ \@minus 2\p@
|
||||
}
|
||||
\renewcommand{\footnotesize}{\@setfontsize\footnotesize\@ixpt\@xpt}
|
||||
\renewcommand{\scriptsize}{\@setfontsize\scriptsize\@viipt\@viiipt}
|
||||
\renewcommand{\tiny}{\@setfontsize\tiny\@vipt\@viipt}
|
||||
\renewcommand{\large}{\@setfontsize\large\@xiipt{14}}
|
||||
\renewcommand{\Large}{\@setfontsize\Large\@xivpt{16}}
|
||||
\renewcommand{\LARGE}{\@setfontsize\LARGE\@xviipt{20}}
|
||||
\renewcommand{\huge}{\@setfontsize\huge\@xxpt{23}}
|
||||
\renewcommand{\Huge}{\@setfontsize\Huge\@xxvpt{28}}
|
||||
|
||||
class TestConnectionTracing:
|
||||
def test_direct_link(self):
|
||||
wf = {
|
||||
"1": {"class_type": "CLIPTextEncode", "inputs": {"text": "x"}},
|
||||
"2": {"class_type": "KSampler",
|
||||
"inputs": {"positive": ["1", 0], "negative": ["1", 0]}},
|
||||
}
|
||||
assert trace_to_node(wf, ["1", 0]) == "1"
|
||||
% sections with less space
|
||||
\providecommand{\section}{}
|
||||
\renewcommand{\section}{%
|
||||
\@startsection{section}{1}{\z@}%
|
||||
{-2.0ex \@plus -0.5ex \@minus -0.2ex}%
|
||||
{ 1.5ex \@plus 0.3ex \@minus 0.2ex}%
|
||||
{\large\bf\raggedright}%
|
||||
}
|
||||
\providecommand{\subsection}{}
|
||||
\renewcommand{\subsection}{%
|
||||
\@startsection{subsection}{2}{\z@}%
|
||||
{-1.8ex \@plus -0.5ex \@minus -0.2ex}%
|
||||
{ 0.8ex \@plus 0.2ex}%
|
||||
{\normalsize\bf\raggedright}%
|
||||
}
|
||||
\providecommand{\subsubsection}{}
|
||||
\renewcommand{\subsubsection}{%
|
||||
\@startsection{subsubsection}{3}{\z@}%
|
||||
{-1.5ex \@plus -0.5ex \@minus -0.2ex}%
|
||||
{ 0.5ex \@plus 0.2ex}%
|
||||
{\normalsize\bf\raggedright}%
|
||||
}
|
||||
\providecommand{\paragraph}{}
|
||||
\renewcommand{\paragraph}{%
|
||||
\@startsection{paragraph}{4}{\z@}%
|
||||
{1.5ex \@plus 0.5ex \@minus 0.2ex}%
|
||||
{-1em}%
|
||||
{\normalsize\bf}%
|
||||
}
|
||||
\providecommand{\subparagraph}{}
|
||||
\renewcommand{\subparagraph}{%
|
||||
\@startsection{subparagraph}{5}{\z@}%
|
||||
{1.5ex \@plus 0.5ex \@minus 0.2ex}%
|
||||
{-1em}%
|
||||
{\normalsize\bf}%
|
||||
}
|
||||
\providecommand{\subsubsubsection}{}
|
||||
\renewcommand{\subsubsubsection}{%
|
||||
\vskip5pt{\noindent\normalsize\rm\raggedright}%
|
||||
}
|
||||
|
||||
def test_through_reroute(self):
|
||||
wf = {
|
||||
"1": {"class_type": "CLIPTextEncode", "inputs": {"text": "x"}},
|
||||
"2": {"class_type": "Reroute", "inputs": {"input": ["1", 0]}},
|
||||
"3": {"class_type": "Reroute", "inputs": {"input": ["2", 0]}},
|
||||
}
|
||||
assert trace_to_node(wf, ["3", 0]) == "1"
|
||||
% float placement
|
||||
\renewcommand{\topfraction }{0.85}
|
||||
\renewcommand{\bottomfraction }{0.4}
|
||||
\renewcommand{\textfraction }{0.1}
|
||||
\renewcommand{\floatpagefraction}{0.7}
|
||||
|
||||
def test_circular_safe(self):
|
||||
wf = {
|
||||
"1": {"class_type": "Reroute", "inputs": {"input": ["2", 0]}},
|
||||
"2": {"class_type": "Reroute", "inputs": {"input": ["1", 0]}},
|
||||
}
|
||||
# Should hit max_hops without infinite loop
|
||||
result = trace_to_node(wf, ["1", 0], max_hops=5)
|
||||
assert result in {"1", "2"} # any node, just don't hang
|
||||
\newlength{\@neuripsabovecaptionskip}\setlength{\@neuripsabovecaptionskip}{7\p@}
|
||||
\newlength{\@neuripsbelowcaptionskip}\setlength{\@neuripsbelowcaptionskip}{\z@}
|
||||
|
||||
\setlength{\abovecaptionskip}{\@neuripsabovecaptionskip}
|
||||
\setlength{\belowcaptionskip}{\@neuripsbelowcaptionskip}
|
||||
|
||||
% swap above/belowcaptionskip lengths for tables
|
||||
\renewenvironment{table}
|
||||
{\setlength{\abovecaptionskip}{\@neuripsbelowcaptionskip}%
|
||||
\setlength{\belowcaptionskip}{\@neuripsabovecaptionskip}%
|
||||
\@float{table}}
|
||||
{\end@float}
|
||||
|
||||
% footnote formatting
|
||||
\setlength{\footnotesep }{6.65\p@}
|
||||
\setlength{\skip\footins}{9\p@ \@plus 4\p@ \@minus 2\p@}
|
||||
\renewcommand{\footnoterule}{\kern-3\p@ \hrule width 12pc \kern 2.6\p@}
|
||||
\setcounter{footnote}{0}
|
||||
|
||||
% paragraph formatting
|
||||
\setlength{\parindent}{\z@}
|
||||
\setlength{\parskip }{5.5\p@}
|
||||
|
||||
% list formatting
|
||||
\setlength{\topsep }{4\p@ \@plus 1\p@ \@minus 2\p@}
|
||||
\setlength{\partopsep }{1\p@ \@plus 0.5\p@ \@minus 0.5\p@}
|
||||
\setlength{\itemsep }{2\p@ \@plus 1\p@ \@minus 0.5\p@}
|
||||
\setlength{\parsep }{2\p@ \@plus 1\p@ \@minus 0.5\p@}
|
||||
\setlength{\leftmargin }{3pc}
|
||||
\setlength{\leftmargini }{\leftmargin}
|
||||
\setlength{\leftmarginii }{2em}
|
||||
\setlength{\leftmarginiii}{1.5em}
|
||||
\setlength{\leftmarginiv }{1.0em}
|
||||
\setlength{\leftmarginv }{0.5em}
|
||||
\def\@listi {\leftmargin\leftmargini}
|
||||
\def\@listii {\leftmargin\leftmarginii
|
||||
\labelwidth\leftmarginii
|
||||
\advance\labelwidth-\labelsep
|
||||
\topsep 2\p@ \@plus 1\p@ \@minus 0.5\p@
|
||||
\parsep 1\p@ \@plus 0.5\p@ \@minus 0.5\p@
|
||||
\itemsep \parsep}
|
||||
\def\@listiii{\leftmargin\leftmarginiii
|
||||
\labelwidth\leftmarginiii
|
||||
\advance\labelwidth-\labelsep
|
||||
\topsep 1\p@ \@plus 0.5\p@ \@minus 0.5\p@
|
||||
\parsep \z@
|
||||
\partopsep 0.5\p@ \@plus 0\p@ \@minus 0.5\p@
|
||||
\itemsep \topsep}
|
||||
\def\@listiv {\leftmargin\leftmarginiv
|
||||
\labelwidth\leftmarginiv
|
||||
\advance\labelwidth-\labelsep}
|
||||
\def\@listv {\leftmargin\leftmarginv
|
||||
\labelwidth\leftmarginv
|
||||
\advance\labelwidth-\labelsep}
|
||||
\def\@listvi {\leftmargin\leftmarginvi
|
||||
\labelwidth\leftmarginvi
|
||||
\advance\labelwidth-\labelsep}
|
||||
|
||||
% create title
|
||||
\providecommand{\maketitle}{}
|
||||
\renewcommand{\maketitle}{%
|
||||
\par
|
||||
\begingroup
|
||||
\renewcommand{\thefootnote}{\fnsymbol{footnote}}
|
||||
% for perfect author name centering
|
||||
\renewcommand{\@makefnmark}{\hbox to \z@{$^{\@thefnmark}$\hss}}
|
||||
% The footnote-mark was overlapping the footnote-text,
|
||||
% added the following to fix this problem (MK)
|
||||
\long\def\@makefntext##1{%
|
||||
\parindent 1em\noindent
|
||||
\hbox to 1.8em{\hss $\m@th ^{\@thefnmark}$}##1
|
||||
}
|
||||
\thispagestyle{empty}
|
||||
\@maketitle
|
||||
\@thanks
|
||||
\@notice
|
||||
\endgroup
|
||||
\let\maketitle\relax
|
||||
\let\thanks\relax
|
||||
}
|
||||
|
||||
% rules for title box at top of first page
|
||||
\newcommand{\@toptitlebar}{
|
||||
\hrule height 4\p@
|
||||
\vskip 0.25in
|
||||
\vskip -\parskip%
|
||||
}
|
||||
\newcommand{\@bottomtitlebar}{
|
||||
\vskip 0.29in
|
||||
\vskip -\parskip
|
||||
\hrule height 1\p@
|
||||
\vskip 0.09in%
|
||||
}
|
||||
|
||||
% create title (includes both anonymized and non-anonymized versions)
|
||||
\providecommand{\@maketitle}{}
|
||||
\renewcommand{\@maketitle}{%
|
||||
\vbox{%
|
||||
\hsize\textwidth
|
||||
\linewidth\hsize
|
||||
\vskip 0.1in
|
||||
\@toptitlebar
|
||||
\centering
|
||||
{\LARGE\bf \@title\par}
|
||||
\@bottomtitlebar
|
||||
\if@submission
|
||||
\begin{tabular}[t]{c}\bf\rule{\z@}{24\p@}
|
||||
Anonymous Author(s) \\
|
||||
Affiliation \\
|
||||
Address \\
|
||||
\texttt{email} \\
|
||||
\end{tabular}%
|
||||
\else
|
||||
\def\And{%
|
||||
\end{tabular}\hfil\linebreak[0]\hfil%
|
||||
\begin{tabular}[t]{c}\bf\rule{\z@}{24\p@}\ignorespaces%
|
||||
}
|
||||
\def\AND{%
|
||||
\end{tabular}\hfil\linebreak[4]\hfil%
|
||||
\begin{tabular}[t]{c}\bf\rule{\z@}{24\p@}\ignorespaces%
|
||||
}
|
||||
\begin{tabular}[t]{c}\bf\rule{\z@}{24\p@}\@author\end{tabular}%
|
||||
\fi
|
||||
\vskip 0.3in \@minus 0.1in
|
||||
}
|
||||
}
|
||||
|
||||
% add conference notice to bottom of first page
|
||||
\newcommand{\ftype@noticebox}{8}
|
||||
\newcommand{\@notice}{%
|
||||
% give a bit of extra room back to authors on first page
|
||||
\enlargethispage{2\baselineskip}%
|
||||
\@float{noticebox}[b]%
|
||||
\footnotesize\@noticestring%
|
||||
\end@float%
|
||||
}
|
||||
|
||||
% abstract styling
|
||||
\renewenvironment{abstract}%
|
||||
{%
|
||||
\vskip 0.075in%
|
||||
\centerline%
|
||||
{\large\bf Abstract}%
|
||||
\vspace{0.5ex}%
|
||||
\begin{quote}%
|
||||
}
|
||||
{
|
||||
\par%
|
||||
\end{quote}%
|
||||
\vskip 1ex%
|
||||
}
|
||||
|
||||
% For the paper checklist
|
||||
\newcommand{\answerYes}[1][]{\textcolor{blue}{[Yes] #1}}
|
||||
\newcommand{\answerNo}[1][]{\textcolor{orange}{[No] #1}}
|
||||
\newcommand{\answerNA}[1][]{\textcolor{gray}{[NA] #1}}
|
||||
\newcommand{\answerTODO}[1][]{\textcolor{red}{\bf [TODO]}}
|
||||
\newcommand{\justificationTODO}[1][]{\textcolor{red}{\bf [TODO]}}
|
||||
|
||||
% handle tweaks for camera-ready copy vs. submission copy
|
||||
\if@preprint
|
||||
\newcommand{\@noticestring}{%
|
||||
Preprint. Under review.%
|
||||
}
|
||||
\else
|
||||
\if@neuripsfinal
|
||||
\newcommand{\@noticestring}{%
|
||||
\@neuripsordinal\/ Conference on Neural Information Processing Systems
|
||||
(NeurIPS \@neuripsyear).%, \@neuripslocation.%
|
||||
}
|
||||
\else
|
||||
\newcommand{\@noticestring}{%
|
||||
Submitted to \@neuripsordinal\/ Conference on Neural Information
|
||||
Processing Systems (NeurIPS \@neuripsyear). Do not distribute.%
|
||||
}
|
||||
|
||||
% hide the acknowledgements
|
||||
\NewEnviron{hide}{}
|
||||
\let\ack\hide
|
||||
\let\endack\endhide
|
||||
|
||||
% line numbers for submission
|
||||
\RequirePackage{lineno}
|
||||
\linenumbers
|
||||
|
||||
% fix incompatibilities between lineno and amsmath, if required, by
|
||||
% transparently wrapping linenomath environments around amsmath
|
||||
% environments
|
||||
\AtBeginDocument{%
|
||||
\@ifpackageloaded{amsmath}{%
|
||||
\newcommand*\patchAmsMathEnvironmentForLineno[1]{%
|
||||
\expandafter\let\csname old#1\expandafter\endcsname\csname #1\endcsname
|
||||
\expandafter\let\csname oldend#1\expandafter\endcsname\csname end#1\endcsname
|
||||
\renewenvironment{#1}%
|
||||
{\linenomath\csname old#1\endcsname}%
|
||||
{\csname oldend#1\endcsname\endlinenomath}%
|
||||
}%
|
||||
\newcommand*\patchBothAmsMathEnvironmentsForLineno[1]{%
|
||||
\patchAmsMathEnvironmentForLineno{#1}%
|
||||
\patchAmsMathEnvironmentForLineno{#1*}%
|
||||
}%
|
||||
\patchBothAmsMathEnvironmentsForLineno{equation}%
|
||||
\patchBothAmsMathEnvironmentsForLineno{align}%
|
||||
\patchBothAmsMathEnvironmentsForLineno{flalign}%
|
||||
\patchBothAmsMathEnvironmentsForLineno{alignat}%
|
||||
\patchBothAmsMathEnvironmentsForLineno{gather}%
|
||||
\patchBothAmsMathEnvironmentsForLineno{multline}%
|
||||
}
|
||||
{}
|
||||
}
|
||||
\fi
|
||||
\fi
|
||||
|
||||
|
||||
class TestPositiveNegativeDetection:
|
||||
def test_basic(self, sd15_workflow):
|
||||
# In sd15_workflow.json node 6 is positive, node 7 is negative
|
||||
assert find_positive_prompt_node(sd15_workflow) == "6"
|
||||
assert find_negative_prompt_node(sd15_workflow) == "7"
|
||||
|
||||
def test_swapped_order(self):
|
||||
wf = {
|
||||
"3": {"class_type": "KSampler",
|
||||
"inputs": {
|
||||
"positive": ["7", 0], "negative": ["6", 0],
|
||||
"model": ["4", 0], "latent_image": ["5", 0],
|
||||
"seed": 1, "steps": 20, "cfg": 7.5,
|
||||
"sampler_name": "euler", "scheduler": "normal", "denoise": 1.0,
|
||||
}},
|
||||
"4": {"class_type": "CheckpointLoaderSimple", "inputs": {"ckpt_name": "x"}},
|
||||
"5": {"class_type": "EmptyLatentImage", "inputs": {"width": 512, "height": 512, "batch_size": 1}},
|
||||
"6": {"class_type": "CLIPTextEncode", "inputs": {"text": "ugly", "clip": ["4", 1]}},
|
||||
"7": {"class_type": "CLIPTextEncode", "inputs": {"text": "beautiful", "clip": ["4", 1]}},
|
||||
}
|
||||
# Now 7 is the positive (despite higher node ID)
|
||||
assert find_positive_prompt_node(wf) == "7"
|
||||
assert find_negative_prompt_node(wf) == "6"
|
||||
|
||||
|
||||
# =============================================================================
|
||||
# Schema extraction
|
||||
# =============================================================================
|
||||
|
||||
class TestExtractSchema:
|
||||
def test_basic_sd15(self, sd15_workflow):
|
||||
schema = extract_schema(sd15_workflow)
|
||||
params = schema["parameters"]
|
||||
assert "prompt" in params
|
||||
assert "negative_prompt" in params
|
||||
assert "seed" in params
|
||||
assert "steps" in params
|
||||
assert "cfg" in params
|
||||
assert "width" in params
|
||||
assert "height" in params
|
||||
|
||||
def test_prompt_value_correct(self, sd15_workflow):
|
||||
schema = extract_schema(sd15_workflow)
|
||||
# The positive prompt in the example is the landscape one
|
||||
assert "landscape" in schema["parameters"]["prompt"]["value"]
|
||||
assert "ugly" in schema["parameters"]["negative_prompt"]["value"]
|
||||
|
||||
def test_model_dependencies(self, sd15_workflow):
|
||||
schema = extract_schema(sd15_workflow)
|
||||
deps = schema["model_dependencies"]
|
||||
ckpts = [d["value"] for d in deps if d["folder"] == "checkpoints"]
|
||||
assert "v1-5-pruned-emaonly.safetensors" in ckpts
|
||||
|
||||
def test_output_nodes(self, sd15_workflow):
|
||||
schema = extract_schema(sd15_workflow)
|
||||
assert "9" in schema["output_nodes"]
|
||||
|
||||
def test_summary(self, sd15_workflow):
|
||||
schema = extract_schema(sd15_workflow)
|
||||
s = schema["summary"]
|
||||
assert s["has_negative_prompt"] is True
|
||||
assert s["has_seed"] is True
|
||||
assert s["is_video_workflow"] is False
|
||||
assert s["parameter_count"] > 5
|
||||
|
||||
def test_flux_workflow(self, flux_workflow):
|
||||
schema = extract_schema(flux_workflow)
|
||||
# Flux uses RandomNoise for seed
|
||||
assert schema["summary"]["has_seed"] is True
|
||||
# Flux has only positive prompt (no negative encoder)
|
||||
assert schema["summary"]["has_negative_prompt"] is False
|
||||
|
||||
def test_video_detected(self, video_workflow):
|
||||
schema = extract_schema(video_workflow)
|
||||
assert schema["summary"]["is_video_workflow"] is True
|
||||
|
||||
|
||||
class TestEmbeddingDeps:
|
||||
def test_extract_from_prompt(self):
|
||||
wf = {
|
||||
"1": {"class_type": "CheckpointLoaderSimple", "inputs": {"ckpt_name": "x"}},
|
||||
"5": {"class_type": "EmptyLatentImage",
|
||||
"inputs": {"width": 512, "height": 512, "batch_size": 1}},
|
||||
"6": {"class_type": "CLIPTextEncode",
|
||||
"inputs": {
|
||||
"text": "a cat, embedding:goodvibes, embedding:art:1.2",
|
||||
"clip": ["1", 1]
|
||||
}},
|
||||
"7": {"class_type": "CLIPTextEncode",
|
||||
"inputs": {
|
||||
"text": "ugly, embedding:badhands",
|
||||
"clip": ["1", 1]
|
||||
}},
|
||||
"3": {"class_type": "KSampler",
|
||||
"inputs": {
|
||||
"positive": ["6", 0], "negative": ["7", 0],
|
||||
"model": ["1", 0], "latent_image": ["5", 0],
|
||||
"seed": 1, "steps": 20, "cfg": 7.5,
|
||||
"sampler_name": "euler", "scheduler": "normal", "denoise": 1.0,
|
||||
}},
|
||||
"9": {"class_type": "SaveImage", "inputs": {"filename_prefix": "x", "images": ["3", 0]}},
|
||||
}
|
||||
schema = extract_schema(wf)
|
||||
names = [d["embedding_name"] for d in schema["embedding_dependencies"]]
|
||||
assert sorted(names) == ["art", "badhands", "goodvibes"]
|
||||
|
||||
|
||||
class TestDuplicateDeduplication:
|
||||
def test_two_ksamplers_get_unique_names(self):
|
||||
wf = {
|
||||
"1": {"class_type": "CheckpointLoaderSimple", "inputs": {"ckpt_name": "x"}},
|
||||
"5": {"class_type": "EmptyLatentImage",
|
||||
"inputs": {"width": 512, "height": 512, "batch_size": 1}},
|
||||
"6": {"class_type": "CLIPTextEncode", "inputs": {"text": "a", "clip": ["1", 1]}},
|
||||
"7": {"class_type": "CLIPTextEncode", "inputs": {"text": "b", "clip": ["1", 1]}},
|
||||
"3": {"class_type": "KSampler",
|
||||
"inputs": {
|
||||
"positive": ["6", 0], "negative": ["7", 0],
|
||||
"model": ["1", 0], "latent_image": ["5", 0],
|
||||
"seed": 42, "steps": 20, "cfg": 7.5,
|
||||
"sampler_name": "euler", "scheduler": "normal", "denoise": 1.0,
|
||||
}},
|
||||
"4": {"class_type": "KSampler",
|
||||
"inputs": {
|
||||
"positive": ["6", 0], "negative": ["7", 0],
|
||||
"model": ["1", 0], "latent_image": ["5", 0],
|
||||
"seed": 99, "steps": 30, "cfg": 8.0,
|
||||
"sampler_name": "euler", "scheduler": "normal", "denoise": 0.6,
|
||||
}},
|
||||
"9": {"class_type": "SaveImage", "inputs": {"filename_prefix": "x", "images": ["3", 0]}},
|
||||
}
|
||||
schema = extract_schema(wf)
|
||||
params = schema["parameters"]
|
||||
# Both seeds present with disambiguated names
|
||||
seed_keys = [k for k in params if "seed" in k]
|
||||
# Symmetric: both renamed (no bare "seed")
|
||||
assert "seed" not in params
|
||||
assert "seed_3" in params and "seed_4" in params
|
||||
assert params["seed_3"]["value"] == 42
|
||||
assert params["seed_4"]["value"] == 99
|
||||
\endinput
|
||||
|
||||
@@ -1,98 +1,382 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Extract text from documents using pymupdf. Lightweight (~25MB), no models.
|
||||
% partial rewrite of the LaTeX2e package for submissions to the
|
||||
% Conference on Neural Information Processing Systems (NeurIPS):
|
||||
%
|
||||
% - uses more LaTeX conventions
|
||||
% - line numbers at submission time replaced with aligned numbers from
|
||||
% lineno package
|
||||
% - \nipsfinalcopy replaced with [final] package option
|
||||
% - automatically loads times package for authors
|
||||
% - loads natbib automatically; this can be suppressed with the
|
||||
% [nonatbib] package option
|
||||
% - adds foot line to first page identifying the conference
|
||||
% - adds preprint option for submission to e.g. arXiv
|
||||
% - conference acronym modified
|
||||
%
|
||||
% Roman Garnett (garnett@wustl.edu) and the many authors of
|
||||
% nips15submit_e.sty, including MK and drstrip@sandia
|
||||
%
|
||||
% last revision: April 2025
|
||||
|
||||
Usage:
|
||||
python extract_pymupdf.py document.pdf
|
||||
python extract_pymupdf.py document.pdf --markdown
|
||||
python extract_pymupdf.py document.pdf --pages 0-4
|
||||
python extract_pymupdf.py document.pdf --images output_dir/
|
||||
python extract_pymupdf.py document.pdf --tables
|
||||
python extract_pymupdf.py document.pdf --metadata
|
||||
"""
|
||||
import sys
|
||||
import json
|
||||
\NeedsTeXFormat{LaTeX2e}
|
||||
\ProvidesPackage{neurips_2025}[2025/04/02 NeurIPS 2025 submission/camera-ready style file]
|
||||
|
||||
def extract_text(path, pages=None):
|
||||
import pymupdf
|
||||
doc = pymupdf.open(path)
|
||||
page_range = range(len(doc)) if pages is None else pages
|
||||
for i in page_range:
|
||||
if i < len(doc):
|
||||
print(f"\n--- Page {i+1}/{len(doc)} ---\n")
|
||||
print(doc[i].get_text())
|
||||
% declare final option, which creates camera-ready copy
|
||||
\newif\if@neuripsfinal\@neuripsfinalfalse
|
||||
\DeclareOption{final}{
|
||||
\@neuripsfinaltrue
|
||||
}
|
||||
|
||||
def extract_markdown(path, pages=None):
|
||||
import pymupdf4llm
|
||||
md = pymupdf4llm.to_markdown(path, pages=pages)
|
||||
print(md)
|
||||
% declare nonatbib option, which does not load natbib in case of
|
||||
% package clash (users can pass options to natbib via
|
||||
% \PassOptionsToPackage)
|
||||
\newif\if@natbib\@natbibtrue
|
||||
\DeclareOption{nonatbib}{
|
||||
\@natbibfalse
|
||||
}
|
||||
|
||||
def extract_tables(path):
|
||||
import pymupdf
|
||||
doc = pymupdf.open(path)
|
||||
for i, page in enumerate(doc):
|
||||
tables = page.find_tables()
|
||||
for j, table in enumerate(tables.tables):
|
||||
print(f"\n--- Page {i+1}, Table {j+1} ---\n")
|
||||
df = table.to_pandas()
|
||||
print(df.to_markdown(index=False))
|
||||
% declare preprint option, which creates a preprint version ready for
|
||||
% upload to, e.g., arXiv
|
||||
\newif\if@preprint\@preprintfalse
|
||||
\DeclareOption{preprint}{
|
||||
\@preprinttrue
|
||||
}
|
||||
|
||||
def extract_images(path, output_dir):
|
||||
import pymupdf
|
||||
from pathlib import Path
|
||||
Path(output_dir).mkdir(parents=True, exist_ok=True)
|
||||
doc = pymupdf.open(path)
|
||||
count = 0
|
||||
for i, page in enumerate(doc):
|
||||
for img_idx, img in enumerate(page.get_images(full=True)):
|
||||
xref = img[0]
|
||||
pix = pymupdf.Pixmap(doc, xref)
|
||||
if pix.n >= 5:
|
||||
pix = pymupdf.Pixmap(pymupdf.csRGB, pix)
|
||||
out_path = f"{output_dir}/page{i+1}_img{img_idx+1}.png"
|
||||
pix.save(out_path)
|
||||
count += 1
|
||||
print(f"Extracted {count} images to {output_dir}/")
|
||||
\ProcessOptions\relax
|
||||
|
||||
def show_metadata(path):
|
||||
import pymupdf
|
||||
doc = pymupdf.open(path)
|
||||
print(json.dumps({
|
||||
"pages": len(doc),
|
||||
"title": doc.metadata.get("title", ""),
|
||||
"author": doc.metadata.get("author", ""),
|
||||
"subject": doc.metadata.get("subject", ""),
|
||||
"creator": doc.metadata.get("creator", ""),
|
||||
"producer": doc.metadata.get("producer", ""),
|
||||
"format": doc.metadata.get("format", ""),
|
||||
}, indent=2))
|
||||
% determine whether this is an anonymized submission
|
||||
\newif\if@submission\@submissiontrue
|
||||
\if@neuripsfinal\@submissionfalse\fi
|
||||
\if@preprint\@submissionfalse\fi
|
||||
|
||||
if __name__ == "__main__":
|
||||
args = sys.argv[1:]
|
||||
if not args or args[0] in {"-h", "--help"}:
|
||||
print(__doc__)
|
||||
sys.exit(0)
|
||||
% fonts
|
||||
\renewcommand{\rmdefault}{ptm}
|
||||
\renewcommand{\sfdefault}{phv}
|
||||
|
||||
path = args[0]
|
||||
pages = None
|
||||
% change this every year for notice string at bottom
|
||||
\newcommand{\@neuripsordinal}{39th}
|
||||
\newcommand{\@neuripsyear}{2025}
|
||||
\newcommand{\@neuripslocation}{San Diego}
|
||||
|
||||
if "--pages" in args:
|
||||
idx = args.index("--pages")
|
||||
p = args[idx + 1]
|
||||
if "-" in p:
|
||||
start, end = p.split("-")
|
||||
pages = list(range(int(start), int(end) + 1))
|
||||
else:
|
||||
pages = [int(p)]
|
||||
% acknowledgments
|
||||
\usepackage{environ}
|
||||
\newcommand{\acksection}{\section*{Acknowledgments and Disclosure of Funding}}
|
||||
\NewEnviron{ack}{%
|
||||
\acksection
|
||||
\BODY
|
||||
}
|
||||
|
||||
if "--metadata" in args:
|
||||
show_metadata(path)
|
||||
elif "--tables" in args:
|
||||
extract_tables(path)
|
||||
elif "--images" in args:
|
||||
idx = args.index("--images")
|
||||
output_dir = args[idx + 1] if idx + 1 < len(args) else "./images"
|
||||
extract_images(path, output_dir)
|
||||
elif "--markdown" in args:
|
||||
extract_markdown(path, pages=pages)
|
||||
else:
|
||||
extract_text(path, pages=pages)
|
||||
|
||||
% load natbib unless told otherwise
|
||||
\if@natbib
|
||||
\RequirePackage{natbib}
|
||||
\fi
|
||||
|
||||
% set page geometry
|
||||
\usepackage[verbose=true,letterpaper]{geometry}
|
||||
\AtBeginDocument{
|
||||
\newgeometry{
|
||||
textheight=9in,
|
||||
textwidth=5.5in,
|
||||
top=1in,
|
||||
headheight=12pt,
|
||||
headsep=25pt,
|
||||
footskip=30pt
|
||||
}
|
||||
\@ifpackageloaded{fullpage}
|
||||
{\PackageWarning{neurips_2025}{fullpage package not allowed! Overwriting formatting.}}
|
||||
{}
|
||||
}
|
||||
|
||||
\widowpenalty=10000
|
||||
\clubpenalty=10000
|
||||
\flushbottom
|
||||
\sloppy
|
||||
|
||||
|
||||
% font sizes with reduced leading
|
||||
\renewcommand{\normalsize}{%
|
||||
\@setfontsize\normalsize\@xpt\@xipt
|
||||
\abovedisplayskip 7\p@ \@plus 2\p@ \@minus 5\p@
|
||||
\abovedisplayshortskip \z@ \@plus 3\p@
|
||||
\belowdisplayskip \abovedisplayskip
|
||||
\belowdisplayshortskip 4\p@ \@plus 3\p@ \@minus 3\p@
|
||||
}
|
||||
\normalsize
|
||||
\renewcommand{\small}{%
|
||||
\@setfontsize\small\@ixpt\@xpt
|
||||
\abovedisplayskip 6\p@ \@plus 1.5\p@ \@minus 4\p@
|
||||
\abovedisplayshortskip \z@ \@plus 2\p@
|
||||
\belowdisplayskip \abovedisplayskip
|
||||
\belowdisplayshortskip 3\p@ \@plus 2\p@ \@minus 2\p@
|
||||
}
|
||||
\renewcommand{\footnotesize}{\@setfontsize\footnotesize\@ixpt\@xpt}
|
||||
\renewcommand{\scriptsize}{\@setfontsize\scriptsize\@viipt\@viiipt}
|
||||
\renewcommand{\tiny}{\@setfontsize\tiny\@vipt\@viipt}
|
||||
\renewcommand{\large}{\@setfontsize\large\@xiipt{14}}
|
||||
\renewcommand{\Large}{\@setfontsize\Large\@xivpt{16}}
|
||||
\renewcommand{\LARGE}{\@setfontsize\LARGE\@xviipt{20}}
|
||||
\renewcommand{\huge}{\@setfontsize\huge\@xxpt{23}}
|
||||
\renewcommand{\Huge}{\@setfontsize\Huge\@xxvpt{28}}
|
||||
|
||||
% sections with less space
|
||||
\providecommand{\section}{}
|
||||
\renewcommand{\section}{%
|
||||
\@startsection{section}{1}{\z@}%
|
||||
{-2.0ex \@plus -0.5ex \@minus -0.2ex}%
|
||||
{ 1.5ex \@plus 0.3ex \@minus 0.2ex}%
|
||||
{\large\bf\raggedright}%
|
||||
}
|
||||
\providecommand{\subsection}{}
|
||||
\renewcommand{\subsection}{%
|
||||
\@startsection{subsection}{2}{\z@}%
|
||||
{-1.8ex \@plus -0.5ex \@minus -0.2ex}%
|
||||
{ 0.8ex \@plus 0.2ex}%
|
||||
{\normalsize\bf\raggedright}%
|
||||
}
|
||||
\providecommand{\subsubsection}{}
|
||||
\renewcommand{\subsubsection}{%
|
||||
\@startsection{subsubsection}{3}{\z@}%
|
||||
{-1.5ex \@plus -0.5ex \@minus -0.2ex}%
|
||||
{ 0.5ex \@plus 0.2ex}%
|
||||
{\normalsize\bf\raggedright}%
|
||||
}
|
||||
\providecommand{\paragraph}{}
|
||||
\renewcommand{\paragraph}{%
|
||||
\@startsection{paragraph}{4}{\z@}%
|
||||
{1.5ex \@plus 0.5ex \@minus 0.2ex}%
|
||||
{-1em}%
|
||||
{\normalsize\bf}%
|
||||
}
|
||||
\providecommand{\subparagraph}{}
|
||||
\renewcommand{\subparagraph}{%
|
||||
\@startsection{subparagraph}{5}{\z@}%
|
||||
{1.5ex \@plus 0.5ex \@minus 0.2ex}%
|
||||
{-1em}%
|
||||
{\normalsize\bf}%
|
||||
}
|
||||
\providecommand{\subsubsubsection}{}
|
||||
\renewcommand{\subsubsubsection}{%
|
||||
\vskip5pt{\noindent\normalsize\rm\raggedright}%
|
||||
}
|
||||
|
||||
% float placement
|
||||
\renewcommand{\topfraction }{0.85}
|
||||
\renewcommand{\bottomfraction }{0.4}
|
||||
\renewcommand{\textfraction }{0.1}
|
||||
\renewcommand{\floatpagefraction}{0.7}
|
||||
|
||||
\newlength{\@neuripsabovecaptionskip}\setlength{\@neuripsabovecaptionskip}{7\p@}
|
||||
\newlength{\@neuripsbelowcaptionskip}\setlength{\@neuripsbelowcaptionskip}{\z@}
|
||||
|
||||
\setlength{\abovecaptionskip}{\@neuripsabovecaptionskip}
|
||||
\setlength{\belowcaptionskip}{\@neuripsbelowcaptionskip}
|
||||
|
||||
% swap above/belowcaptionskip lengths for tables
|
||||
\renewenvironment{table}
|
||||
{\setlength{\abovecaptionskip}{\@neuripsbelowcaptionskip}%
|
||||
\setlength{\belowcaptionskip}{\@neuripsabovecaptionskip}%
|
||||
\@float{table}}
|
||||
{\end@float}
|
||||
|
||||
% footnote formatting
|
||||
\setlength{\footnotesep }{6.65\p@}
|
||||
\setlength{\skip\footins}{9\p@ \@plus 4\p@ \@minus 2\p@}
|
||||
\renewcommand{\footnoterule}{\kern-3\p@ \hrule width 12pc \kern 2.6\p@}
|
||||
\setcounter{footnote}{0}
|
||||
|
||||
% paragraph formatting
|
||||
\setlength{\parindent}{\z@}
|
||||
\setlength{\parskip }{5.5\p@}
|
||||
|
||||
% list formatting
|
||||
\setlength{\topsep }{4\p@ \@plus 1\p@ \@minus 2\p@}
|
||||
\setlength{\partopsep }{1\p@ \@plus 0.5\p@ \@minus 0.5\p@}
|
||||
\setlength{\itemsep }{2\p@ \@plus 1\p@ \@minus 0.5\p@}
|
||||
\setlength{\parsep }{2\p@ \@plus 1\p@ \@minus 0.5\p@}
|
||||
\setlength{\leftmargin }{3pc}
|
||||
\setlength{\leftmargini }{\leftmargin}
|
||||
\setlength{\leftmarginii }{2em}
|
||||
\setlength{\leftmarginiii}{1.5em}
|
||||
\setlength{\leftmarginiv }{1.0em}
|
||||
\setlength{\leftmarginv }{0.5em}
|
||||
\def\@listi {\leftmargin\leftmargini}
|
||||
\def\@listii {\leftmargin\leftmarginii
|
||||
\labelwidth\leftmarginii
|
||||
\advance\labelwidth-\labelsep
|
||||
\topsep 2\p@ \@plus 1\p@ \@minus 0.5\p@
|
||||
\parsep 1\p@ \@plus 0.5\p@ \@minus 0.5\p@
|
||||
\itemsep \parsep}
|
||||
\def\@listiii{\leftmargin\leftmarginiii
|
||||
\labelwidth\leftmarginiii
|
||||
\advance\labelwidth-\labelsep
|
||||
\topsep 1\p@ \@plus 0.5\p@ \@minus 0.5\p@
|
||||
\parsep \z@
|
||||
\partopsep 0.5\p@ \@plus 0\p@ \@minus 0.5\p@
|
||||
\itemsep \topsep}
|
||||
\def\@listiv {\leftmargin\leftmarginiv
|
||||
\labelwidth\leftmarginiv
|
||||
\advance\labelwidth-\labelsep}
|
||||
\def\@listv {\leftmargin\leftmarginv
|
||||
\labelwidth\leftmarginv
|
||||
\advance\labelwidth-\labelsep}
|
||||
\def\@listvi {\leftmargin\leftmarginvi
|
||||
\labelwidth\leftmarginvi
|
||||
\advance\labelwidth-\labelsep}
|
||||
|
||||
% create title
|
||||
\providecommand{\maketitle}{}
|
||||
\renewcommand{\maketitle}{%
|
||||
\par
|
||||
\begingroup
|
||||
\renewcommand{\thefootnote}{\fnsymbol{footnote}}
|
||||
% for perfect author name centering
|
||||
\renewcommand{\@makefnmark}{\hbox to \z@{$^{\@thefnmark}$\hss}}
|
||||
% The footnote-mark was overlapping the footnote-text,
|
||||
% added the following to fix this problem (MK)
|
||||
\long\def\@makefntext##1{%
|
||||
\parindent 1em\noindent
|
||||
\hbox to 1.8em{\hss $\m@th ^{\@thefnmark}$}##1
|
||||
}
|
||||
\thispagestyle{empty}
|
||||
\@maketitle
|
||||
\@thanks
|
||||
\@notice
|
||||
\endgroup
|
||||
\let\maketitle\relax
|
||||
\let\thanks\relax
|
||||
}
|
||||
|
||||
% rules for title box at top of first page
|
||||
\newcommand{\@toptitlebar}{
|
||||
\hrule height 4\p@
|
||||
\vskip 0.25in
|
||||
\vskip -\parskip%
|
||||
}
|
||||
\newcommand{\@bottomtitlebar}{
|
||||
\vskip 0.29in
|
||||
\vskip -\parskip
|
||||
\hrule height 1\p@
|
||||
\vskip 0.09in%
|
||||
}
|
||||
|
||||
% create title (includes both anonymized and non-anonymized versions)
|
||||
\providecommand{\@maketitle}{}
|
||||
\renewcommand{\@maketitle}{%
|
||||
\vbox{%
|
||||
\hsize\textwidth
|
||||
\linewidth\hsize
|
||||
\vskip 0.1in
|
||||
\@toptitlebar
|
||||
\centering
|
||||
{\LARGE\bf \@title\par}
|
||||
\@bottomtitlebar
|
||||
\if@submission
|
||||
\begin{tabular}[t]{c}\bf\rule{\z@}{24\p@}
|
||||
Anonymous Author(s) \\
|
||||
Affiliation \\
|
||||
Address \\
|
||||
\texttt{email} \\
|
||||
\end{tabular}%
|
||||
\else
|
||||
\def\And{%
|
||||
\end{tabular}\hfil\linebreak[0]\hfil%
|
||||
\begin{tabular}[t]{c}\bf\rule{\z@}{24\p@}\ignorespaces%
|
||||
}
|
||||
\def\AND{%
|
||||
\end{tabular}\hfil\linebreak[4]\hfil%
|
||||
\begin{tabular}[t]{c}\bf\rule{\z@}{24\p@}\ignorespaces%
|
||||
}
|
||||
\begin{tabular}[t]{c}\bf\rule{\z@}{24\p@}\@author\end{tabular}%
|
||||
\fi
|
||||
\vskip 0.3in \@minus 0.1in
|
||||
}
|
||||
}
|
||||
|
||||
% add conference notice to bottom of first page
|
||||
\newcommand{\ftype@noticebox}{8}
|
||||
\newcommand{\@notice}{%
|
||||
% give a bit of extra room back to authors on first page
|
||||
\enlargethispage{2\baselineskip}%
|
||||
\@float{noticebox}[b]%
|
||||
\footnotesize\@noticestring%
|
||||
\end@float%
|
||||
}
|
||||
|
||||
% abstract styling
|
||||
\renewenvironment{abstract}%
|
||||
{%
|
||||
\vskip 0.075in%
|
||||
\centerline%
|
||||
{\large\bf Abstract}%
|
||||
\vspace{0.5ex}%
|
||||
\begin{quote}%
|
||||
}
|
||||
{
|
||||
\par%
|
||||
\end{quote}%
|
||||
\vskip 1ex%
|
||||
}
|
||||
|
||||
% For the paper checklist
|
||||
\newcommand{\answerYes}[1][]{\textcolor{blue}{[Yes] #1}}
|
||||
\newcommand{\answerNo}[1][]{\textcolor{orange}{[No] #1}}
|
||||
\newcommand{\answerNA}[1][]{\textcolor{gray}{[NA] #1}}
|
||||
\newcommand{\answerTODO}[1][]{\textcolor{red}{\bf [TODO]}}
|
||||
\newcommand{\justificationTODO}[1][]{\textcolor{red}{\bf [TODO]}}
|
||||
|
||||
% handle tweaks for camera-ready copy vs. submission copy
|
||||
\if@preprint
|
||||
\newcommand{\@noticestring}{%
|
||||
Preprint. Under review.%
|
||||
}
|
||||
\else
|
||||
\if@neuripsfinal
|
||||
\newcommand{\@noticestring}{%
|
||||
\@neuripsordinal\/ Conference on Neural Information Processing Systems
|
||||
(NeurIPS \@neuripsyear).%, \@neuripslocation.%
|
||||
}
|
||||
\else
|
||||
\newcommand{\@noticestring}{%
|
||||
Submitted to \@neuripsordinal\/ Conference on Neural Information
|
||||
Processing Systems (NeurIPS \@neuripsyear). Do not distribute.%
|
||||
}
|
||||
|
||||
% hide the acknowledgements
|
||||
\NewEnviron{hide}{}
|
||||
\let\ack\hide
|
||||
\let\endack\endhide
|
||||
|
||||
% line numbers for submission
|
||||
\RequirePackage{lineno}
|
||||
\linenumbers
|
||||
|
||||
% fix incompatibilities between lineno and amsmath, if required, by
|
||||
% transparently wrapping linenomath environments around amsmath
|
||||
% environments
|
||||
\AtBeginDocument{%
|
||||
\@ifpackageloaded{amsmath}{%
|
||||
\newcommand*\patchAmsMathEnvironmentForLineno[1]{%
|
||||
\expandafter\let\csname old#1\expandafter\endcsname\csname #1\endcsname
|
||||
\expandafter\let\csname oldend#1\expandafter\endcsname\csname end#1\endcsname
|
||||
\renewenvironment{#1}%
|
||||
{\linenomath\csname old#1\endcsname}%
|
||||
{\csname oldend#1\endcsname\endlinenomath}%
|
||||
}%
|
||||
\newcommand*\patchBothAmsMathEnvironmentsForLineno[1]{%
|
||||
\patchAmsMathEnvironmentForLineno{#1}%
|
||||
\patchAmsMathEnvironmentForLineno{#1*}%
|
||||
}%
|
||||
\patchBothAmsMathEnvironmentsForLineno{equation}%
|
||||
\patchBothAmsMathEnvironmentsForLineno{align}%
|
||||
\patchBothAmsMathEnvironmentsForLineno{flalign}%
|
||||
\patchBothAmsMathEnvironmentsForLineno{alignat}%
|
||||
\patchBothAmsMathEnvironmentsForLineno{gather}%
|
||||
\patchBothAmsMathEnvironmentsForLineno{multline}%
|
||||
}
|
||||
{}
|
||||
}
|
||||
\fi
|
||||
\fi
|
||||
|
||||
|
||||
\endinput
|
||||
|
||||
Binary file not shown.
@@ -1,98 +1,382 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Extract text from documents using pymupdf. Lightweight (~25MB), no models.
|
||||
% partial rewrite of the LaTeX2e package for submissions to the
|
||||
% Conference on Neural Information Processing Systems (NeurIPS):
|
||||
%
|
||||
% - uses more LaTeX conventions
|
||||
% - line numbers at submission time replaced with aligned numbers from
|
||||
% lineno package
|
||||
% - \nipsfinalcopy replaced with [final] package option
|
||||
% - automatically loads times package for authors
|
||||
% - loads natbib automatically; this can be suppressed with the
|
||||
% [nonatbib] package option
|
||||
% - adds foot line to first page identifying the conference
|
||||
% - adds preprint option for submission to e.g. arXiv
|
||||
% - conference acronym modified
|
||||
%
|
||||
% Roman Garnett (garnett@wustl.edu) and the many authors of
|
||||
% nips15submit_e.sty, including MK and drstrip@sandia
|
||||
%
|
||||
% last revision: April 2025
|
||||
|
||||
Usage:
|
||||
python extract_pymupdf.py document.pdf
|
||||
python extract_pymupdf.py document.pdf --markdown
|
||||
python extract_pymupdf.py document.pdf --pages 0-4
|
||||
python extract_pymupdf.py document.pdf --images output_dir/
|
||||
python extract_pymupdf.py document.pdf --tables
|
||||
python extract_pymupdf.py document.pdf --metadata
|
||||
"""
|
||||
import sys
|
||||
import json
|
||||
\NeedsTeXFormat{LaTeX2e}
|
||||
\ProvidesPackage{neurips_2025}[2025/04/02 NeurIPS 2025 submission/camera-ready style file]
|
||||
|
||||
def extract_text(path, pages=None):
|
||||
import pymupdf
|
||||
doc = pymupdf.open(path)
|
||||
page_range = range(len(doc)) if pages is None else pages
|
||||
for i in page_range:
|
||||
if i < len(doc):
|
||||
print(f"\n--- Page {i+1}/{len(doc)} ---\n")
|
||||
print(doc[i].get_text())
|
||||
% declare final option, which creates camera-ready copy
|
||||
\newif\if@neuripsfinal\@neuripsfinalfalse
|
||||
\DeclareOption{final}{
|
||||
\@neuripsfinaltrue
|
||||
}
|
||||
|
||||
def extract_markdown(path, pages=None):
|
||||
import pymupdf4llm
|
||||
md = pymupdf4llm.to_markdown(path, pages=pages)
|
||||
print(md)
|
||||
% declare nonatbib option, which does not load natbib in case of
|
||||
% package clash (users can pass options to natbib via
|
||||
% \PassOptionsToPackage)
|
||||
\newif\if@natbib\@natbibtrue
|
||||
\DeclareOption{nonatbib}{
|
||||
\@natbibfalse
|
||||
}
|
||||
|
||||
def extract_tables(path):
|
||||
import pymupdf
|
||||
doc = pymupdf.open(path)
|
||||
for i, page in enumerate(doc):
|
||||
tables = page.find_tables()
|
||||
for j, table in enumerate(tables.tables):
|
||||
print(f"\n--- Page {i+1}, Table {j+1} ---\n")
|
||||
df = table.to_pandas()
|
||||
print(df.to_markdown(index=False))
|
||||
% declare preprint option, which creates a preprint version ready for
|
||||
% upload to, e.g., arXiv
|
||||
\newif\if@preprint\@preprintfalse
|
||||
\DeclareOption{preprint}{
|
||||
\@preprinttrue
|
||||
}
|
||||
|
||||
def extract_images(path, output_dir):
|
||||
import pymupdf
|
||||
from pathlib import Path
|
||||
Path(output_dir).mkdir(parents=True, exist_ok=True)
|
||||
doc = pymupdf.open(path)
|
||||
count = 0
|
||||
for i, page in enumerate(doc):
|
||||
for img_idx, img in enumerate(page.get_images(full=True)):
|
||||
xref = img[0]
|
||||
pix = pymupdf.Pixmap(doc, xref)
|
||||
if pix.n >= 5:
|
||||
pix = pymupdf.Pixmap(pymupdf.csRGB, pix)
|
||||
out_path = f"{output_dir}/page{i+1}_img{img_idx+1}.png"
|
||||
pix.save(out_path)
|
||||
count += 1
|
||||
print(f"Extracted {count} images to {output_dir}/")
|
||||
\ProcessOptions\relax
|
||||
|
||||
def show_metadata(path):
|
||||
import pymupdf
|
||||
doc = pymupdf.open(path)
|
||||
print(json.dumps({
|
||||
"pages": len(doc),
|
||||
"title": doc.metadata.get("title", ""),
|
||||
"author": doc.metadata.get("author", ""),
|
||||
"subject": doc.metadata.get("subject", ""),
|
||||
"creator": doc.metadata.get("creator", ""),
|
||||
"producer": doc.metadata.get("producer", ""),
|
||||
"format": doc.metadata.get("format", ""),
|
||||
}, indent=2))
|
||||
% determine whether this is an anonymized submission
|
||||
\newif\if@submission\@submissiontrue
|
||||
\if@neuripsfinal\@submissionfalse\fi
|
||||
\if@preprint\@submissionfalse\fi
|
||||
|
||||
if __name__ == "__main__":
|
||||
args = sys.argv[1:]
|
||||
if not args or args[0] in {"-h", "--help"}:
|
||||
print(__doc__)
|
||||
sys.exit(0)
|
||||
% fonts
|
||||
\renewcommand{\rmdefault}{ptm}
|
||||
\renewcommand{\sfdefault}{phv}
|
||||
|
||||
path = args[0]
|
||||
pages = None
|
||||
% change this every year for notice string at bottom
|
||||
\newcommand{\@neuripsordinal}{39th}
|
||||
\newcommand{\@neuripsyear}{2025}
|
||||
\newcommand{\@neuripslocation}{San Diego}
|
||||
|
||||
if "--pages" in args:
|
||||
idx = args.index("--pages")
|
||||
p = args[idx + 1]
|
||||
if "-" in p:
|
||||
start, end = p.split("-")
|
||||
pages = list(range(int(start), int(end) + 1))
|
||||
else:
|
||||
pages = [int(p)]
|
||||
% acknowledgments
|
||||
\usepackage{environ}
|
||||
\newcommand{\acksection}{\section*{Acknowledgments and Disclosure of Funding}}
|
||||
\NewEnviron{ack}{%
|
||||
\acksection
|
||||
\BODY
|
||||
}
|
||||
|
||||
if "--metadata" in args:
|
||||
show_metadata(path)
|
||||
elif "--tables" in args:
|
||||
extract_tables(path)
|
||||
elif "--images" in args:
|
||||
idx = args.index("--images")
|
||||
output_dir = args[idx + 1] if idx + 1 < len(args) else "./images"
|
||||
extract_images(path, output_dir)
|
||||
elif "--markdown" in args:
|
||||
extract_markdown(path, pages=pages)
|
||||
else:
|
||||
extract_text(path, pages=pages)
|
||||
|
||||
% load natbib unless told otherwise
|
||||
\if@natbib
|
||||
\RequirePackage{natbib}
|
||||
\fi
|
||||
|
||||
% set page geometry
|
||||
\usepackage[verbose=true,letterpaper]{geometry}
|
||||
\AtBeginDocument{
|
||||
\newgeometry{
|
||||
textheight=9in,
|
||||
textwidth=5.5in,
|
||||
top=1in,
|
||||
headheight=12pt,
|
||||
headsep=25pt,
|
||||
footskip=30pt
|
||||
}
|
||||
\@ifpackageloaded{fullpage}
|
||||
{\PackageWarning{neurips_2025}{fullpage package not allowed! Overwriting formatting.}}
|
||||
{}
|
||||
}
|
||||
|
||||
\widowpenalty=10000
|
||||
\clubpenalty=10000
|
||||
\flushbottom
|
||||
\sloppy
|
||||
|
||||
|
||||
% font sizes with reduced leading
|
||||
\renewcommand{\normalsize}{%
|
||||
\@setfontsize\normalsize\@xpt\@xipt
|
||||
\abovedisplayskip 7\p@ \@plus 2\p@ \@minus 5\p@
|
||||
\abovedisplayshortskip \z@ \@plus 3\p@
|
||||
\belowdisplayskip \abovedisplayskip
|
||||
\belowdisplayshortskip 4\p@ \@plus 3\p@ \@minus 3\p@
|
||||
}
|
||||
\normalsize
|
||||
\renewcommand{\small}{%
|
||||
\@setfontsize\small\@ixpt\@xpt
|
||||
\abovedisplayskip 6\p@ \@plus 1.5\p@ \@minus 4\p@
|
||||
\abovedisplayshortskip \z@ \@plus 2\p@
|
||||
\belowdisplayskip \abovedisplayskip
|
||||
\belowdisplayshortskip 3\p@ \@plus 2\p@ \@minus 2\p@
|
||||
}
|
||||
\renewcommand{\footnotesize}{\@setfontsize\footnotesize\@ixpt\@xpt}
|
||||
\renewcommand{\scriptsize}{\@setfontsize\scriptsize\@viipt\@viiipt}
|
||||
\renewcommand{\tiny}{\@setfontsize\tiny\@vipt\@viipt}
|
||||
\renewcommand{\large}{\@setfontsize\large\@xiipt{14}}
|
||||
\renewcommand{\Large}{\@setfontsize\Large\@xivpt{16}}
|
||||
\renewcommand{\LARGE}{\@setfontsize\LARGE\@xviipt{20}}
|
||||
\renewcommand{\huge}{\@setfontsize\huge\@xxpt{23}}
|
||||
\renewcommand{\Huge}{\@setfontsize\Huge\@xxvpt{28}}
|
||||
|
||||
% sections with less space
|
||||
\providecommand{\section}{}
|
||||
\renewcommand{\section}{%
|
||||
\@startsection{section}{1}{\z@}%
|
||||
{-2.0ex \@plus -0.5ex \@minus -0.2ex}%
|
||||
{ 1.5ex \@plus 0.3ex \@minus 0.2ex}%
|
||||
{\large\bf\raggedright}%
|
||||
}
|
||||
\providecommand{\subsection}{}
|
||||
\renewcommand{\subsection}{%
|
||||
\@startsection{subsection}{2}{\z@}%
|
||||
{-1.8ex \@plus -0.5ex \@minus -0.2ex}%
|
||||
{ 0.8ex \@plus 0.2ex}%
|
||||
{\normalsize\bf\raggedright}%
|
||||
}
|
||||
\providecommand{\subsubsection}{}
|
||||
\renewcommand{\subsubsection}{%
|
||||
\@startsection{subsubsection}{3}{\z@}%
|
||||
{-1.5ex \@plus -0.5ex \@minus -0.2ex}%
|
||||
{ 0.5ex \@plus 0.2ex}%
|
||||
{\normalsize\bf\raggedright}%
|
||||
}
|
||||
\providecommand{\paragraph}{}
|
||||
\renewcommand{\paragraph}{%
|
||||
\@startsection{paragraph}{4}{\z@}%
|
||||
{1.5ex \@plus 0.5ex \@minus 0.2ex}%
|
||||
{-1em}%
|
||||
{\normalsize\bf}%
|
||||
}
|
||||
\providecommand{\subparagraph}{}
|
||||
\renewcommand{\subparagraph}{%
|
||||
\@startsection{subparagraph}{5}{\z@}%
|
||||
{1.5ex \@plus 0.5ex \@minus 0.2ex}%
|
||||
{-1em}%
|
||||
{\normalsize\bf}%
|
||||
}
|
||||
\providecommand{\subsubsubsection}{}
|
||||
\renewcommand{\subsubsubsection}{%
|
||||
\vskip5pt{\noindent\normalsize\rm\raggedright}%
|
||||
}
|
||||
|
||||
% float placement
|
||||
\renewcommand{\topfraction }{0.85}
|
||||
\renewcommand{\bottomfraction }{0.4}
|
||||
\renewcommand{\textfraction }{0.1}
|
||||
\renewcommand{\floatpagefraction}{0.7}
|
||||
|
||||
\newlength{\@neuripsabovecaptionskip}\setlength{\@neuripsabovecaptionskip}{7\p@}
|
||||
\newlength{\@neuripsbelowcaptionskip}\setlength{\@neuripsbelowcaptionskip}{\z@}
|
||||
|
||||
\setlength{\abovecaptionskip}{\@neuripsabovecaptionskip}
|
||||
\setlength{\belowcaptionskip}{\@neuripsbelowcaptionskip}
|
||||
|
||||
% swap above/belowcaptionskip lengths for tables
|
||||
\renewenvironment{table}
|
||||
{\setlength{\abovecaptionskip}{\@neuripsbelowcaptionskip}%
|
||||
\setlength{\belowcaptionskip}{\@neuripsabovecaptionskip}%
|
||||
\@float{table}}
|
||||
{\end@float}
|
||||
|
||||
% footnote formatting
|
||||
\setlength{\footnotesep }{6.65\p@}
|
||||
\setlength{\skip\footins}{9\p@ \@plus 4\p@ \@minus 2\p@}
|
||||
\renewcommand{\footnoterule}{\kern-3\p@ \hrule width 12pc \kern 2.6\p@}
|
||||
\setcounter{footnote}{0}
|
||||
|
||||
% paragraph formatting
|
||||
\setlength{\parindent}{\z@}
|
||||
\setlength{\parskip }{5.5\p@}
|
||||
|
||||
% list formatting
|
||||
\setlength{\topsep }{4\p@ \@plus 1\p@ \@minus 2\p@}
|
||||
\setlength{\partopsep }{1\p@ \@plus 0.5\p@ \@minus 0.5\p@}
|
||||
\setlength{\itemsep }{2\p@ \@plus 1\p@ \@minus 0.5\p@}
|
||||
\setlength{\parsep }{2\p@ \@plus 1\p@ \@minus 0.5\p@}
|
||||
\setlength{\leftmargin }{3pc}
|
||||
\setlength{\leftmargini }{\leftmargin}
|
||||
\setlength{\leftmarginii }{2em}
|
||||
\setlength{\leftmarginiii}{1.5em}
|
||||
\setlength{\leftmarginiv }{1.0em}
|
||||
\setlength{\leftmarginv }{0.5em}
|
||||
\def\@listi {\leftmargin\leftmargini}
|
||||
\def\@listii {\leftmargin\leftmarginii
|
||||
\labelwidth\leftmarginii
|
||||
\advance\labelwidth-\labelsep
|
||||
\topsep 2\p@ \@plus 1\p@ \@minus 0.5\p@
|
||||
\parsep 1\p@ \@plus 0.5\p@ \@minus 0.5\p@
|
||||
\itemsep \parsep}
|
||||
\def\@listiii{\leftmargin\leftmarginiii
|
||||
\labelwidth\leftmarginiii
|
||||
\advance\labelwidth-\labelsep
|
||||
\topsep 1\p@ \@plus 0.5\p@ \@minus 0.5\p@
|
||||
\parsep \z@
|
||||
\partopsep 0.5\p@ \@plus 0\p@ \@minus 0.5\p@
|
||||
\itemsep \topsep}
|
||||
\def\@listiv {\leftmargin\leftmarginiv
|
||||
\labelwidth\leftmarginiv
|
||||
\advance\labelwidth-\labelsep}
|
||||
\def\@listv {\leftmargin\leftmarginv
|
||||
\labelwidth\leftmarginv
|
||||
\advance\labelwidth-\labelsep}
|
||||
\def\@listvi {\leftmargin\leftmarginvi
|
||||
\labelwidth\leftmarginvi
|
||||
\advance\labelwidth-\labelsep}
|
||||
|
||||
% create title
|
||||
\providecommand{\maketitle}{}
|
||||
\renewcommand{\maketitle}{%
|
||||
\par
|
||||
\begingroup
|
||||
\renewcommand{\thefootnote}{\fnsymbol{footnote}}
|
||||
% for perfect author name centering
|
||||
\renewcommand{\@makefnmark}{\hbox to \z@{$^{\@thefnmark}$\hss}}
|
||||
% The footnote-mark was overlapping the footnote-text,
|
||||
% added the following to fix this problem (MK)
|
||||
\long\def\@makefntext##1{%
|
||||
\parindent 1em\noindent
|
||||
\hbox to 1.8em{\hss $\m@th ^{\@thefnmark}$}##1
|
||||
}
|
||||
\thispagestyle{empty}
|
||||
\@maketitle
|
||||
\@thanks
|
||||
\@notice
|
||||
\endgroup
|
||||
\let\maketitle\relax
|
||||
\let\thanks\relax
|
||||
}
|
||||
|
||||
% rules for title box at top of first page
|
||||
\newcommand{\@toptitlebar}{
|
||||
\hrule height 4\p@
|
||||
\vskip 0.25in
|
||||
\vskip -\parskip%
|
||||
}
|
||||
\newcommand{\@bottomtitlebar}{
|
||||
\vskip 0.29in
|
||||
\vskip -\parskip
|
||||
\hrule height 1\p@
|
||||
\vskip 0.09in%
|
||||
}
|
||||
|
||||
% create title (includes both anonymized and non-anonymized versions)
|
||||
\providecommand{\@maketitle}{}
|
||||
\renewcommand{\@maketitle}{%
|
||||
\vbox{%
|
||||
\hsize\textwidth
|
||||
\linewidth\hsize
|
||||
\vskip 0.1in
|
||||
\@toptitlebar
|
||||
\centering
|
||||
{\LARGE\bf \@title\par}
|
||||
\@bottomtitlebar
|
||||
\if@submission
|
||||
\begin{tabular}[t]{c}\bf\rule{\z@}{24\p@}
|
||||
Anonymous Author(s) \\
|
||||
Affiliation \\
|
||||
Address \\
|
||||
\texttt{email} \\
|
||||
\end{tabular}%
|
||||
\else
|
||||
\def\And{%
|
||||
\end{tabular}\hfil\linebreak[0]\hfil%
|
||||
\begin{tabular}[t]{c}\bf\rule{\z@}{24\p@}\ignorespaces%
|
||||
}
|
||||
\def\AND{%
|
||||
\end{tabular}\hfil\linebreak[4]\hfil%
|
||||
\begin{tabular}[t]{c}\bf\rule{\z@}{24\p@}\ignorespaces%
|
||||
}
|
||||
\begin{tabular}[t]{c}\bf\rule{\z@}{24\p@}\@author\end{tabular}%
|
||||
\fi
|
||||
\vskip 0.3in \@minus 0.1in
|
||||
}
|
||||
}
|
||||
|
||||
% add conference notice to bottom of first page
|
||||
\newcommand{\ftype@noticebox}{8}
|
||||
\newcommand{\@notice}{%
|
||||
% give a bit of extra room back to authors on first page
|
||||
\enlargethispage{2\baselineskip}%
|
||||
\@float{noticebox}[b]%
|
||||
\footnotesize\@noticestring%
|
||||
\end@float%
|
||||
}
|
||||
|
||||
% abstract styling
|
||||
\renewenvironment{abstract}%
|
||||
{%
|
||||
\vskip 0.075in%
|
||||
\centerline%
|
||||
{\large\bf Abstract}%
|
||||
\vspace{0.5ex}%
|
||||
\begin{quote}%
|
||||
}
|
||||
{
|
||||
\par%
|
||||
\end{quote}%
|
||||
\vskip 1ex%
|
||||
}
|
||||
|
||||
% For the paper checklist
|
||||
\newcommand{\answerYes}[1][]{\textcolor{blue}{[Yes] #1}}
|
||||
\newcommand{\answerNo}[1][]{\textcolor{orange}{[No] #1}}
|
||||
\newcommand{\answerNA}[1][]{\textcolor{gray}{[NA] #1}}
|
||||
\newcommand{\answerTODO}[1][]{\textcolor{red}{\bf [TODO]}}
|
||||
\newcommand{\justificationTODO}[1][]{\textcolor{red}{\bf [TODO]}}
|
||||
|
||||
% handle tweaks for camera-ready copy vs. submission copy
|
||||
\if@preprint
|
||||
\newcommand{\@noticestring}{%
|
||||
Preprint. Under review.%
|
||||
}
|
||||
\else
|
||||
\if@neuripsfinal
|
||||
\newcommand{\@noticestring}{%
|
||||
\@neuripsordinal\/ Conference on Neural Information Processing Systems
|
||||
(NeurIPS \@neuripsyear).%, \@neuripslocation.%
|
||||
}
|
||||
\else
|
||||
\newcommand{\@noticestring}{%
|
||||
Submitted to \@neuripsordinal\/ Conference on Neural Information
|
||||
Processing Systems (NeurIPS \@neuripsyear). Do not distribute.%
|
||||
}
|
||||
|
||||
% hide the acknowledgements
|
||||
\NewEnviron{hide}{}
|
||||
\let\ack\hide
|
||||
\let\endack\endhide
|
||||
|
||||
% line numbers for submission
|
||||
\RequirePackage{lineno}
|
||||
\linenumbers
|
||||
|
||||
% fix incompatibilities between lineno and amsmath, if required, by
|
||||
% transparently wrapping linenomath environments around amsmath
|
||||
% environments
|
||||
\AtBeginDocument{%
|
||||
\@ifpackageloaded{amsmath}{%
|
||||
\newcommand*\patchAmsMathEnvironmentForLineno[1]{%
|
||||
\expandafter\let\csname old#1\expandafter\endcsname\csname #1\endcsname
|
||||
\expandafter\let\csname oldend#1\expandafter\endcsname\csname end#1\endcsname
|
||||
\renewenvironment{#1}%
|
||||
{\linenomath\csname old#1\endcsname}%
|
||||
{\csname oldend#1\endcsname\endlinenomath}%
|
||||
}%
|
||||
\newcommand*\patchBothAmsMathEnvironmentsForLineno[1]{%
|
||||
\patchAmsMathEnvironmentForLineno{#1}%
|
||||
\patchAmsMathEnvironmentForLineno{#1*}%
|
||||
}%
|
||||
\patchBothAmsMathEnvironmentsForLineno{equation}%
|
||||
\patchBothAmsMathEnvironmentsForLineno{align}%
|
||||
\patchBothAmsMathEnvironmentsForLineno{flalign}%
|
||||
\patchBothAmsMathEnvironmentsForLineno{alignat}%
|
||||
\patchBothAmsMathEnvironmentsForLineno{gather}%
|
||||
\patchBothAmsMathEnvironmentsForLineno{multline}%
|
||||
}
|
||||
{}
|
||||
}
|
||||
\fi
|
||||
\fi
|
||||
|
||||
|
||||
\endinput
|
||||
|
||||
Reference in New Issue
Block a user