diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..2828bcf --- /dev/null +++ b/.gitignore @@ -0,0 +1,6 @@ +# Ignore LaTeX output files. +*.aux +*.bbl +*.bst +*.log +*.out diff --git a/README.md b/README.md index 0109736..fac5ee9 100644 --- a/README.md +++ b/README.md @@ -12,6 +12,19 @@ template for ICML and are going to add templates for other conferences and journals during calendar year. - [International Conference on Machine Learning (ICML)](icml). -- Neural Information Processing System (NeurIPS). +- [Neural Information Processing System (NeurIPS)](neurips). - International Conference on Learning Representations (ICLR). - Association for the Advancement of Artificial Intelligence (AAAI). + +## Utilities + +Typst of version 0.10.0 does not produce colored annotations. In order to +mitigate the issue, we add [a simple script](colorize-annotations.py) to the +repository. The script is plain and simple. One can use it as follows. + +```shell +./colorize-annotations.py \ + neurips/example-paper.typst.pdf neurips/example-paper-colored.typst.pdf +``` + +It is written with PyMuPDF library and inserts colored annotation. diff --git a/colorize-annotations.py b/colorize-annotations.py new file mode 100755 index 0000000..baab962 --- /dev/null +++ b/colorize-annotations.py @@ -0,0 +1,169 @@ +#!/usr/bin/env python +"""Simple script based on MuPDF bindings for colorizing annotation in documents +produced by Typst v0.10.0 (or may be newer). +""" + +from argparse import ArgumentParser, Namespace +from dataclasses import astuple, dataclass +from os import unlink +from pathlib import Path +from shutil import move +from tempfile import NamedTemporaryFile +from typing import Any, Self + +import fitz +from fitz import Document, Page + +DEFAULT_STROKE_CYAN = (0.0, 1.0, 1.0) + +DEFAULT_STROKE_RED = (1.0, 0.0, 0.0) + +parser = ArgumentParser(description=__doc__) +parser.add_argument('source', type=Path, help='path to original document') +parser.add_argument('target', type=Path, help='path to output document') + + +@dataclass +class Point: + + x: float + + y: float + + def __add__(self, other: Self) -> Self: + return self.__class__(self.x + other.x, self.y + other.y) + + def __iadd__(self, other: Self) -> Self: + self.x += other.x + self.y += other.y + return self + + +@dataclass +class Rect: + + top_left: Point + + bot_right: Point + + @classmethod + def from_points(cls, x0: float, y0: float, x1: float, y1: float) -> Self: + return cls(Point(x0, y0), Point(x1, y1)) + + @classmethod + def from_postscript(cls, value: str) -> Self: + points = [float(x) for x in value[1:-1].split()] + return cls.from_points(*points) + + def __add__(self, other) -> Self: + return self.__class__(self.top_left + other.top_left, + self.bot_right + other.bot_right) + + def __iadd__(self, other) -> Self: + self.top_left += other.top_left + self.bot_right += other.bot_right + return self + + def to_points(self) -> tuple[float, float, float, float]: + return astuple(self.top_left) + astuple(self.bot_right) + + def to_postscript(self) -> str: + points = self.to_points() + coords = ' '.join(f'{point:g}' for point in points) + return f'[{coords}]' + + +DEFAULT_SPACING = Rect.from_points(-0.5, -2.5, 0.5, 1.5) + + +@dataclass +class Style: + + stroke: tuple[float, float, float] + + spacing: Rect + + +DEFAULT_STYLES = { + 'external': Style(stroke=DEFAULT_STROKE_CYAN, spacing=DEFAULT_SPACING), + 'internal': Style(stroke=DEFAULT_STROKE_RED, spacing=DEFAULT_SPACING), +} + + +def enumerate_links(doc: Document): + page: Page + for ix, page in enumerate(doc): + for link in page.get_links(): + yield ix, page, link + + +def colorize_link(doc: Document, link: dict[str, Any], styles=None): + # If there is no valid xref then it is embedded annotation. Skip it. + if (xref := link['xref']) is None or xref <= 0: + return + + styles = styles or DEFAULT_STYLES + if 'uri' in link: + style = styles['external'] + else: + style = styles['internal'] + + stroke_str = ' '.join(f'{x:g}' for x in style.stroke) + doc.xref_set_key(xref, 'H', '/I') + doc.xref_set_key(xref, 'C', f'[{stroke_str}]') + doc.xref_set_key(xref, 'BS', '<< /W 1 /S /S >>') + + # Adjust bounding box of annotation. + type_, value = doc.xref_get_key(xref, 'Rect') + if type_ != 'array': + print(f'Attribute `Rect` of xref={xref} is not array.') + return + rect = Rect.from_postscript(value) + rect += style.spacing + doc.xref_set_key(xref, 'Rect', rect.to_postscript()) + + +def colorize(source: Path, target: Path, styles=None): + doc: Document = fitz.open(source) + + # Enumerate all available links and create new link with the same + # attributes. The issue is that Typst writes embeddable annotations which + # does not have xref. This makes impossible to modify annotation attributes + # inplace. + for _, page, link in enumerate_links(doc): + new_link = {**link} + new_link.pop('xref', None) + new_link.pop('zoom', None) + new_link.pop('id', None) + page.insert_link(new_link) + + # As we duplicated annotations, we can update their appearence. + total_pages = len(doc) + for ix, page in enumerate(doc, 1): + print(f'{ix}/{total_pages}: {page}') + for link in page.links(): + colorize_link(doc, link, styles) + + # If source and target pathes are equal then write to temporary and then + # replace the original one. + if target != source: + target.parent.mkdir(exist_ok=True, parents=True) + doc.save(target, garbage=3, deflate=True) + doc.close() + else: + prefix = '.' + target.name.removesuffix('.pdf') + '-' + with NamedTemporaryFile(mode='w', dir=target.parent, prefix=prefix, + suffix='.pdf', delete=False) as tempfile: + unlink(tempfile.name) + doc.save(tempfile.name, garbage=3, deflate=True) + doc.close() + move(tempfile.name, source) # Copy back. + + +def main(): + ns: Namespace = parser.parse_args() + colorize(ns.source, ns.target) + + +if __name__ == '__main__': + main() diff --git a/neurips/README.md b/neurips/README.md new file mode 100644 index 0000000..2b03928 --- /dev/null +++ b/neurips/README.md @@ -0,0 +1,53 @@ +# Neural Information Processing Systems (NeurIPS) 2023 + +## Example Papers + +Here are an example paper in [LaTeX][1] and in [Typst][2]. + +## Issues + +- The biggest and the most important issues is related to line ruler. We are + not aware of universal method for numbering lines in the main body of a + paper. + +- There is an issue in Typst with spacing between figures and between figure + with floating placement. The issue is that there is no way to specify gap + between subsequent figures. In order to have behaviour similar to original + LaTeX template, one should consider direct spacing adjacemnt with `v(-1em)` + as follows. + + ```typst + #figure( + rect(width: 4.25cm, height: 4.25cm, stroke: 0.4pt), + caption: [Sample figure caption.#v(-1em)], + placement: top, + ) + #figure( + rect(width: 4.25cm, height: 4.25cm, stroke: 0.4pt), + caption: [Sample figure caption.], + placement: top, + ) + ``` + +- Another issue is related to Typst's inablity to produce colored annotation. + In order to mitigte the issue, we add a script which modifies annotations and + make them colored. + + ```shell + ../colorize-annotations.py \ + example-paper.typst.pdf example-paper-colored.typst.pdf + ``` + + See [README.md][3] for details. + +- NeurIPS 2023 instructions discuss bibliography in vague terms. Namely, there + is not specific requirements. Thus we stick to `ieee` bibliography style + since we found it in several accepted papers and it is similar to that in the + example paper. + +- It is unclear how to render notice in the bottom of the title page in case of + final (`accepted: true`) or preprint (`accepted: none`) submission. + +[1]: example-paper.latex.pdf +[2]: example-paper.typst.pdf +[3]: ../#colored-annotations diff --git a/neurips/example-paper.latex.pdf b/neurips/example-paper.latex.pdf new file mode 100644 index 0000000..1d0c746 Binary files /dev/null and b/neurips/example-paper.latex.pdf differ diff --git a/neurips/example-paper.typst.pdf b/neurips/example-paper.typst.pdf new file mode 100644 index 0000000..b404cb6 Binary files /dev/null and b/neurips/example-paper.typst.pdf differ diff --git a/neurips/logo.typ b/neurips/logo.typ new file mode 100644 index 0000000..31c6bfc --- /dev/null +++ b/neurips/logo.typ @@ -0,0 +1,18 @@ +#let kern(length) = h(length, weak: true) +#let TeX = style(styles => { + let e = measure(text("E"), styles) + let T = "T" + let E = text(baseline: e.height / 2, "E") + let X = "X" + box(T + kern(-0.1667em) + E + kern(-0.125em) + X) +}) +#let LaTeX = style(styles => { + let l = measure(text(10pt, "L"), styles) + let a = measure(text(7pt, "A"), styles) + let L = "L" + let A = text(7pt, baseline: a.height - l.height, "A") + box(L + kern(-0.36em) + A + kern(-0.15em) + TeX) +}) +#let LaTeXe = style(styles => { + box(LaTeX + sym.space.sixth + [2#text(baseline: 0.3em, $epsilon$)]) +}) diff --git a/neurips/main.bib b/neurips/main.bib new file mode 100644 index 0000000..b11f853 --- /dev/null +++ b/neurips/main.bib @@ -0,0 +1,25 @@ +@article{alexander1994template, + title={Template-based algorithms for connectionist rule extraction}, + author={Alexander, Jay and Mozer, Michael C}, + journal={Advances in neural information processing systems}, + volume={7}, + year={1994} +} + +@book{bower2012book, + title={The book of GENESIS: exploring realistic neural models with the GEneral NEural SImulation System}, + author={Bower, James M and Beeman, David}, + year={2012}, + publisher={Springer Science \& Business Media} +} + +@article{hasselmo1995dynamics, + title={Dynamics of learning and recall at excitatory recurrent synapses and cholinergic modulation in rat hippocampal region CA3}, + author={Hasselmo, Michael E and Schnell, Eric and Barkai, Edi}, + journal={Journal of Neuroscience}, + volume={15}, + number={7}, + pages={5249--5262}, + year={1995}, + publisher={Soc Neuroscience} +} diff --git a/neurips/main.typ b/neurips/main.typ new file mode 100644 index 0000000..fb03e55 --- /dev/null +++ b/neurips/main.typ @@ -0,0 +1,381 @@ +#import "@preview/tablex:0.0.8": cellx, hlinex, tablex +#import "/neurips2023.typ": * +#import "/logo.typ": LaTeX, LaTeXe, TeX + +#let affls = ( + airi: ("AIRI", "Moscow", "Russia"), + skoltech: ( + department: "AI Center", + institution: "Skoltech", + location: "Moscow", + country: "Russia"), + skoltech2: ( + department: "AI Center", + institution: "Skoltech", + location: "Moscow", + country: "Russia"), +) + +#let authors = ( + (name: "Firstname1 Lastname1", + affl: "skoltech", + email: "author@example.org", + equal: true), + (name: "Firstname2 Lastname2", affl: ("airi", "skoltech"), equal: true), +) + +#show: neurips2023.with( + title: [Formatting Instructions For NeurIPS 2023], + authors: (authors, affls), + keywords: ("Machine Learning", "NeurIPS"), + abstract: [ + The abstract paragraph should be indented ½ inch (3 picas) on both the + left- and right-hand margins. Use 10 point type, with a vertical spacing + (leading) of 11 points. The word *Abstract* must be centered, bold, and in + point size 12. Two line spaces precede the abstract. The abstract must be + limited to one paragraph. + ], + bibliography-file: "main.bib", + bibliography-opts: (title: none, full: true), // Only for example paper. + accepted: false, +) + += Submission of papers to NeurIPS 2023 + +Please read the instructions below carefully and follow them faithfully. +*Important:* This year the checklist will be submitted separately from the main +paper in OpenReview, please review it well ahead of the submission deadline: +#url("https://neurips.cc/public/guides/PaperChecklist") + +== Style + +Papers to be submitted to NeurIPS 2023 must be prepared according to the +instructions presented here. Papers may only be up to *nine* pages long, +including figures. Additional pages _containing only acknowledgments and +references_ are allowed. Papers that exceed the page limit will not be +reviewed, or in any other way considered for presentation at the conference. + +The margins in 2023 are the same as those in previous years. + +Authors are required to use the NeurIPS #LaTeX style files obtainable at the +NeurIPS website as indicated below. Please make sure you use the current files +and not previous versions. Tweaking the style files may be grounds for +rejection. + +== Retrieval of style files + + +The style files for NeurIPS and other conference information are available on +the website at + +#align(center)[ + #url("http://www.neurips.cc/") +] + +The file `neurips_2023.pdf` contains these instructions and illustrates the +various formatting requirements your NeurIPS paper must satisfy. + +The only supported style file for NeurIPS 2023 is `neurips_2023.sty`, rewritten +for #LaTeXe. *Previous style files for #LaTeX 2.09, Microsoft Word, and RTF +are no longer supported!* + +The #LaTeX style file contains three optional arguments: `final`, which creates +a camera-ready copy, `preprint`, which creates a preprint for submission to, +e.g., arXiv, and `nonatbib`, which will not load the `natbib` package for you +in case of package clash. + +#paragraph[Preprint option] If you wish to post a preprint of your work online, +e.g., on arXiv, using the NeurIPS style, please use the `preprint` option. This +will create a nonanonymized version of your work with the text "Preprint. Work +in progress." in the footer. This version may be distributed as you see fit, as +long as you do not say which conference it was submitted to. Please *do not* +use the `final` option, which should *only* be used for papers accepted to +NeurIPS. + +At submission time, please omit the `final` and `preprint` options. This will +anonymize your submission and add line numbers to aid review. Please do _not_ +refer to these line numbers in your paper as they will be removed during +generation of camera-ready copies. + +The file `neurips_2023.tex` may be used as a "shell" for writing your paper. +All you have to do is replace the author, title, abstract, and text of the +paper with your own. + +The formatting instructions contained in these style files are summarized in +Sections~#ref(, supplement: none), #ref(, supplement: +none), and #ref(, supplement: none) below. + += General formatting instructions + +The text must be confined within a rectangle 5.5~inches (33~picas) wide and +9~inches (54~picas) long. The left margin is 1.5~inch (9~picas). Use 10~point +type with a vertical spacing (leading) of 11~points. Times New Roman is the +preferred typeface throughout, and will be selected for you by default. +Paragraphs are separated by ½~line space (5.5 points), with no indentation. + +The paper title should be 17~point, initial caps/lower case, bold, centered +between two horizontal rules. The top rule should be 4~points thick and the +bottom rule should be 1~point thick. Allow ¼~inch space above and below the +title to rules. All pages should start at 1~inch (6~picas) from the top of the +page. + +For the final version, authors' names are set in boldface, and each name is +centered above the corresponding address. The lead author's name is to be +listed first (left-most), and the co-authors' names (if different address) are +set to follow. If there is only one co-author, list both author and co-author +side by side. + +Please pay special attention to the instructions in @others regarding figures, +tables, acknowledgments, and references. + += Headings: first level + +All headings should be lower case (except for first word and proper nouns), +flush left, and bold. + +First-level headings should be in 12-point type. + +== Headings: second level + +Second-level headings should be in 10-point type. + +=== Headings: third level + +Third-level headings should be in 10-point type. + +#paragraph[Paragraphs] There is also a `\paragraph` command available, which +sets the heading in bold, flush left, and inline with the text, with the +heading followed by #1em of space. + += Citations, figures, tables, references + +These instructions apply to everyone. + +== Citations within the text + +The `natbib` package will be loaded for you by default. Citations may be +author/year or numeric, as long as you maintain internal consistency. As to +the format of the references themselves, any style is acceptable as long as it +is used consistently. + +The documentation for `natbib` may be found at + +#align(center)[ + #url("http://mirrors.ctan.org/macros/latex/contrib/natbib/natnotes.pdf") +] + +Of note is the command `\citet`, which produces citations appropriate for use +in inline text. For example, + +```tex + \citet{hasselmo} investigated\dots +``` +produces + +#quote(block: true)[Hasselmo, et al.~(1995) investigated\dots] + +If you wish to load the `natbib` package with options, you may add the +following before loading the `neurips_2023` package: + +```tex + \PassOptionsToPackage{options}{natbib} +``` + +If `natbib` clashes with another package you load, you can add the optional +argument `nonatbib` when loading the style file: + +```tex + \usepackage[nonatbib]{neurips_2023} +``` + +As submission is double blind, refer to your own published work in the third +person. That is, use "In the previous work of Jones et al.~[4]," not "In our +previous work [4]." If you cite your other papers that are not widely available +(e.g., a journal paper under review), use anonymous author names in the +citation, e.g., an author of the form "A.~Anonymous" and include a copy of the +anonymized paper in the supplementary material. + +== Footnotes + +Footnotes should be used sparingly. If you do require a footnote, indicate +footnotes with a number#footnote[Sample of the first footnote.] in the text. +Place the footnotes at the bottom of the page on which they appear. Precede the +footnote with a horizontal rule of 2~inches (12~picas). + +Note that footnotes are properly typeset _after_ punctuation marks.#footnote[As +in this example.] + +== Figures + +#figure( + rect(width: 4.25cm, height: 4.25cm, stroke: 0.4pt), + caption: [Sample figure caption.], + placement: top, +) + +All artwork must be neat, clean, and legible. Lines should be dark enough for +purposes of reproduction. The figure number and caption always appear after the +figure. Place one line space before the figure caption and one line space after +the figure. The figure caption should be lower case (except for first word and +proper nouns); figures are numbered consecutively. + +You may use color figures. However, it is best for the figure captions and the +paper body to be legible if the paper is printed in either black/white or in +color. + +== Tables + +All tables must be centered, neat, clean and legible. The table number and +title always appear before the table. See @sample-table. + +Place one line space before the table title, one line space after the +table title, and one line space after the table. The table title must +be lower case (except for first word and proper nouns); tables are +numbered consecutively. + +Note that publication-quality tables _do not contain vertical rules_. We +strongly suggest the use of the `booktabs` package, which allows for +typesetting high-quality, professional tables: + +#align(center)[ + #url("https://www.ctan.org/pkg/booktabs") +] + +This package was used to typeset @sample-table. + +// Tickness values are taken from booktabs. +#let toprule = hlinex(stroke: (thickness: 0.08em)) +#let bottomrule = toprule +#let midrule = hlinex(stroke: (thickness: 0.05em)) +#let rows = ( + toprule, + cellx(colspan: 2, align: center)[Part], (), [], + hlinex(start: 0, end: 2, stroke: (thickness: 0.05em)), + [Name], [Description], [Size ($mu$)], + midrule, + [Dendrite], [Input terminal ], [$~100$], + [Axon ], [Output terminal], [$~10$], + [Soma ], [Cell body ], [up to $10^6$], + bottomrule, +) + +#figure( + tablex( + columns: 3, + align: left + horizon, + auto-vlines: false, + auto-hlines: false, + header-rows: 2, + ..rows), // TODO(@daskol): Fix gutter between rows in body. + caption: [Sample table title.], + kind: table, + placement: top, +) + +== Math + +Note that display math in bare TeX commands will not create correct line +numbers for submission. Please use LaTeX (or AMSTeX) commands for unnumbered +display math. (You really shouldn't be using $dollar dollar$ anyway; see +#url("https://tex.stackexchange.com/questions/503/why-is-preferable-to") and +#url("https://tex.stackexchange.com/questions/40492/what-are-the-differences-between-align-equation-and-displaymath") +for more information.) + +== Final instructions + +Do not change any aspects of the formatting parameters in the style files. In +particular, do not modify the width or length of the rectangle the text should +fit into, and do not change font sizes (except perhaps in the *References* +section; see below). Please note that pages should be numbered. + += Preparing PDF files + +Please prepare submission files with paper size "US Letter," and not, for +example, "A4." + +Fonts were the main cause of problems in the past years. Your PDF file must only +contain Type 1 or Embedded TrueType fonts. Here are a few instructions to +achieve this. + +- You should directly generate PDF files using `pdflatex`. + +- You can check which fonts a PDF files uses. In Acrobat Reader, select the + menu Files$>$Document Properties$>$Fonts and select Show All Fonts. You can + also use the program `pdffonts` which comes with `xpdf` and is available + out-of-the-box on most Linux machines. + +- `xfig` "patterned" shapes are implemented with bitmap fonts. Use "solid" + shapes instead. + +- The `\bbold` package almost always uses bitmap fonts. You should use the + equivalent AMS Fonts: + + ```tex + \usepackage{amsfonts} + ``` + + followed by, e.g., `\mathbb{R}`, `\mathbb{N}`, or `\mathbb{C}` for $RR$, $NN$ + or $CC$. You can also use the following workaround for reals, natural and + complex: + + ```tex + \newcommand{\RR}{I\!\!R} %real numbers + \newcommand{\Nat}{I\!\!N} %natural numbers + \newcommand{\CC}{I\!\!\!\!C} %complex numbers + ``` + + Note that `amsfonts` is automatically loaded by the `amssymb` package. + +If your file contains Type 3 fonts or non embedded TrueType fonts, we will ask +you to fix it. + +== Margins in #LaTeX + +Most of the margin problems come from figures positioned by hand using +`\special` or other commands. We suggest using the command `\includegraphics` +from the `graphicx` package. Always specify the figure width as a multiple of +the line width as in the example below: + +```tex + \usepackage[pdftex]{graphicx} ... + \includegraphics[width=0.8\linewidth]{myfile.pdf} +``` + +See @tables in the graphics bundle documentation +(#url("http://mirrors.ctan.org/macros/latex/required/graphics/grfguide.pdf")) + +A number of width problems arise when #LaTeX cannot properly hyphenate a line. +please give #LaTeX hyphenation hints using the `\-` command when necessary. + +// note this is the acknowledgments section which is not visible in draft. +#if false [ +use unnumbered first level headings for the acknowledgments. all +acknowledgments go at the end of the paper before the list of references. +moreover, you are required to declare funding (financial activities supporting +the submitted work) and competing interests (related financial activities +outside the submitted work). More information about this disclosure can be +found at: +#url("https://neurips.cc/Conferences/2023/PaperInformation/FundingDisclosure") + +Do *not* include this section in the anonymized submission, only in the final +paper. You can use the `ack` environment provided in the style file to +autmoatically hide this section in the anonymized submission. +] + += Supplementary Material + +Authors may wish to optionally include extra information (complete proofs, +additional experiments and plots) in the appendix. All such materials should be +part of the supplemental material (submitted separately) and should NOT be +included in the main submission. + +// We typset reference section header manualy in order to reproduce example +// paper. No special effort is required (a user should not override +// `bibliography-opts` as well). +#heading(numbering: none)[References] + +References follow the acknowledgments in the camera-ready paper. Use unnumbered +first-level heading for the references. Any choice of citation style is +acceptable as long as you are consistent. It is permissible to reduce the font +size to `small` (9 point) when listing the references. Note that the Reference +section does not count towards the page limit. diff --git a/neurips/neurips2023.typ b/neurips/neurips2023.typ new file mode 100644 index 0000000..7405728 --- /dev/null +++ b/neurips/neurips2023.typ @@ -0,0 +1,483 @@ +// Metrical size of page body. +#let viewport = ( + width: 5.5in, + height: 9in, +) + +// Default font sizes from original LaTeX style file. +#let font-defaults = ( + tiny: 7pt, + scriptsize: 7pt, + footnotesize: 9pt, + small: 9pt, + normalsize: 10pt, + large: 14pt, + Large: 16pt, + LARGE: 20pt, + huge: 23pt, + Huge: 28pt, +) + +// We prefer to use Times New Roman when ever it is possible. +#let font-family = ("Times New Roman", "Nimbus Roman", "TeX Gyre Termes") + +#let font = ( + Large: font-defaults.Large, + footnote: font-defaults.footnotesize, + large: font-defaults.large, + small: font-defaults.small, + normal: font-defaults.normalsize, + script: font-defaults.scriptsize, +) + +#let make_figure_caption(it) = { + set align(center) + block({ + set text(size: font.normal) + it.supplement + if it.numbering != none { + [ ] + it.counter.display(it.numbering) + } + it.separator + [ ] + it.body + }) +} + +#let make_figure(caption_above: false, it) = { + // set align(center + top) + // let body = block(breakable: false, width: 100%, { + let body = { + set text(size: font.normal) + if caption_above { + v(1em, weak: true) // Does not work at the block beginning. + it.caption + } + v(1em, weak: true) + it.body + v(8pt, weak: true) // Original 1em. + if not caption_above { + it.caption + v(1em, weak: true) // Does not work at the block ending. + } + } + + if it.placement == none { + return body + } else { + return place(it.placement + center, body, float: true, clearance: 2.3em) + } +} + +#let anonymous-author = ( + name: "Anonymous Author(s)", + email: "anon.email@example.org", + affl: ("anonymous-affl", ), +) + +#let anonymous-affl = ( + department: none, + institution: "Affilation", + location: "Address", +) + +#let anonymous-notice = [ + Submitted to 37th Conference on Neural Information Processing Systems + (NeurIPS 2023). Do not distribute. +] + +#let arxiv-notice = [Preprint. Under review.] + +#let public-notice = [ + 37th Conference on Neural Information Processing Systems (NeurIPS 2023). +] + +#let format-author-names(authors) = { + // Formats the author's names in a list with commas and a + // final "and". + let author_names = authors.map(author => author.name) + let author-string = if authors.len() == 2 { + author_names.join(" and ") + } else { + author_names.join(", ", last: ", and ") + } + return author_names +} + +#let format-author-name(author, affl2idx, affilated: false) = { + // Sanitize author affilations. + let affl = author.at("affl") + if type(affl) == str { + affl = (affl,) + } + let indices = affl.map(it => str(affl2idx.at(it))).join(" ") + let result = strong(author.name) + if affilated { + result += super(typographic: false, indices) + } + return box(result) +} + +#let format-afflilation(affl) = { + assert(affl.len() > 0, message: "Affilation must be non-empty.") + + // Concatenate terms which representat affilation to a single text. + let affilation = "" + if type(affl) == array { + affilation = affl.join(", ") + } else if type(affl) == dictionary { + let terms = () + if "department" in affl and affl.department != none { + terms.push(affl.department) + } + if "institution" in affl and affl.institution != none { + terms.push(affl.institution) + } + if "location" in affl and affl.location != none { + terms.push(affl.location) + } + if "country" in affl and affl.country != none { + terms.push(affl.country) + } + affilation = terms.filter(it => it.len() > 0).join(", ") + } else { + assert(false, message: "Unexpected execution branch.") + } + + return affilation +} + +#let make-single-author(author, affls, affl2idx) = { + // Sanitize author affilations. + let affl = author.at("affl") + if type(affl) == str { + affl = (affl,) + } + + // Render author name. + let name = format-author-name(author, affl2idx) + // Render affilations. + let affilation = affl + .map(it => format-afflilation(affls.at(it))) + .map(it => box(it)) + .join(" ") + + let lines = (name, affilation) + if "email" in author { + let uri = "mailto:" + author.email + let text = raw(author.email) + lines.push(box(link(uri, text))) + } + + // Combine all parts of author's info. + let body = lines.join([\ ]) + return align(center, body) +} + +#let make-two-authors(authors, affls, affl2idx) = { + let row = authors + .map(it => make-single-author(it, affls, affl2idx)) + .map(it => box(it)) + return align(center, grid(columns: (1fr, 1fr), gutter: 2em, ..row)) +} + +#let make-many-authors(authors, affls, affl2idx) = { + let format-affl(affls, key, index) = { + let affl = affls.at(key) + let affilation = format-afflilation(affl) + let entry = super(typographic: false, [#index]) + affilation + return box(entry) + } + + // Concatenate all author names with affilation superscripts. + let names = authors + .map(it => format-author-name(it, affl2idx, affilated: true)) + + // Concatenate all affilations with superscripts. + let affilations = affl2idx + .pairs() + .map(it => format-affl(affls, ..it)) + + // Concatenate all emails to a single paragraph. + let emails = authors + .filter(it => "email" in it) + .map(it => box(link("mailto:" + it.email, raw(it.email)))) + + // Combine paragraph pieces to single array, then filter and join to + // paragraphs. + let paragraphs = (names, affilations, emails) + .filter(it => it.len() > 0) + .map(it => it.join(h(1em, weak: true))) + .join([#parbreak() ]) + + return align(center, { + pad(left: 1em, right: 1em, paragraphs) + }) +} + +#let make-authors(authors, affls) = { + // Prepare authors and footnote anchors. + let ordered-affls = authors.map(it => it.affl).flatten().dedup() + let affl2idx = ordered-affls.enumerate(start: 1).fold((:), (acc, it) => { + let (ix, affl) = it + acc.insert(affl, ix) + return acc + }) + + if authors.len() == 1 { + return make-single-author(authors.at(0), affls, affl2idx) + } else if authors.len() == 2 { + return make-two-authors(authors, affls, affl2idx) + } else { + return make-many-authors(authors, affls, affl2idx) + } +} + +/** + * neurips2023 + * + * Args: + * accepted: Valid values are `none`, `false`, and `true`. Missing value + * (`none`) is designed to prepare arxiv publication. Default is `false`. + */ +#let neurips2023( + title: [], + authors: (), + keywords: (), + date: auto, + abstract: none, + bibliography-file: none, + bibliography-opts: (:), + accepted: false, + body, +) = { + // Sanitize authors and affilations arguments. + if accepted != none and not accepted { + authors = ((anonymous-author,), (anonymous-affl: anonymous-affl)) + } + let (authors, affls) = authors + + // Configure document metadata. + set document( + title: title, + author: format-author-names(authors), + keywords: keywords, + date: date, + ) + + set page( + paper: "us-letter", + margin: (left: 1.5in, right: 1.5in, + top: 1.0in, bottom: 1in), + footer-descent: 25pt - font.normal, + footer: locate(loc => { + let i = counter(page).at(loc).first() + if i == 1 { + let notice = "" + if accepted == none { + notice = arxiv-notice + } else if accepted { + notice = public-notice + } else { + notice = anonymous-notice + } + return align(center, text(size: 9pt, notice)) + } else { + return align(center, text(size: font.normal, [#i])) + } + }), + ) + + // In the original style, main body font is Times (Type-1) font but we use + // OpenType analogue. + set par(justify: true, leading: 0.55em) + set text(font: font-family, size: font.normal) + + // Configure quotation (similar to LaTeX's `quoting` package). + show quote: set align(left) + show quote: set pad(x: 4em) + show quote: set block(spacing: 1em) // Original 11pt. + + // Configure spacing code snippets as in the original LaTeX. + show raw.where(block: true): set block(spacing: 14pt) // TODO: May be 15pt? + + // Configure bullet lists. + show list: set block(spacing: 15pt) // Original unknown. + set list( + indent: 30pt, // Original 3pc (=36pt) without bullet. + spacing: 8.5pt) + + // Configure footnote. + set footnote.entry( + separator: line(length: 2in, stroke: 0.5pt), + clearance: 6.65pt, + indent: 12pt) // Original 11pt. + + // Configure heading appearence and numbering. + set heading(numbering: "1.1") + show heading: it => { + // Create the heading numbering. + let number = if it.numbering != none { + counter(heading).display(it.numbering) + } + + set align(left) + if it.level == 1 { + // TODO: font.large? + text(size: 12pt, weight: "bold", { + let ex = 7.95pt + v(2.7 * ex, weak: true) + [#number *#it.body*] + v(2 * ex, weak: true) + }) + } else if it.level == 2 { + text(size: font.normal, weight: "bold", { + let ex = 6.62pt + v(2.70 * ex, weak: true) + [#number *#it.body*] + v(2.03 * ex, weak: true) // Original 1ex. + }) + } else if it.level == 3 { + text(size: font.normal, weight: "bold", { + let ex = 6.62pt + v(2.6 * ex, weak: true) + [#number *#it.body*] + v(1.8 * ex, weak: true) // Original -1em. + }) + } + } + + // Configure images and tables appearence. + set figure.caption(separator: [:]) + show figure: set block(breakable: false) + show figure.caption.where(kind: table): it => make_figure_caption(it) + show figure.caption.where(kind: image): it => make_figure_caption(it) + show figure.where(kind: image): it => make_figure(it) + show figure.where(kind: table): it => make_figure(it, caption_above: true) + + // Math equation numbering and referencing. + set math.equation(numbering: "(1)") + show ref: it => { + let eq = math.equation + let el = it.element + if el != none and el.func() == eq { + let numb = numbering( + "1", + ..counter(eq).at(el.location()) + ) + let color = rgb(0%, 8%, 45%) // Originally `mydarkblue`. :D + let content = link(el.location(), text(fill: color, numb)) + [(#content)] + } else { + return it + } + } + + // Configure algorithm rendering. + counter(figure.where(kind: "algorithm")).update(0) + show figure.caption.where(kind: "algorithm"): it => { + strong[#it.supplement #it.counter.display(it.numbering)] + [ ] + it.body + } + show figure.where(kind: "algorithm"): it => { + place(top, float: true, + block(breakable: false, width: 100%, { + set block(spacing: 0em) + line(length: 100%, stroke: (thickness: 0.08em)) + block(spacing: 0.4em, it.caption) // NOTE: No idea why we need it. + line(length: 100%, stroke: (thickness: 0.05em)) + it.body + line(length: 100%, stroke: (thickness: 0.08em)) + }) + ) + } + + // Render title. + block(width: 5.5in, { + // We need to define line widths to reuse them in spacing. + let top-rule-width = 4pt + let bot-rule-width = 1pt + + // Add some space based on line width. + v(0.1in + top-rule-width / 2) + line(length: 100%, stroke: top-rule-width + black) + align(center, text(size: 17pt, weight: "bold", [#title])) + v(-bot-rule-width) + line(length: 100%, stroke: bot-rule-width + black) + }) + + v(0.25in) + + // Render authors. + block(width: 100%, { + set text(size: font.normal) + set par(leading: 4.5pt) + show par: set block(spacing: 1.0em) // Original 11pt. + make-authors(authors, affls) + v(0.3in - 0.1in) + }) + + // Vertical spacing between authors and abstract. + v(6.5pt) // Original 0.075in. + + // Render abstract. + block(width: 100%, { + set text(size: 10pt) + set text(size: font.normal) + set par(leading: 0.43em) // Original 0.55em (or 0.45em?). + + // NeurIPS instruction tels that font size of `Abstract` must equal to 12pt + // but there is not predefined font size. + align(center, text(size: 12pt)[*Abstract*]) + v(0.215em) // Original 0.5ex. + pad(left: 0.5in, right: 0.5in, abstract) + v(0.43em) // Original 0.5ex. + }) + + v(0.43em / 2) // No idea. + + // Render main body + { + // Display body. + set text(size: font.normal) + set par(leading: 0.55em) + set par(leading: 0.43em) + show par: set block(spacing: 1.0em) // Original 11pt. + body + + // Display the bibliography, if any is given. + if bibliography-file != none { + if "title" not in bibliography-opts { + bibliography-opts.title = "References" + } + if "style" not in bibliography-opts { + bibliography-opts.style = "ieee" + } + // NOTE It is allowed to reduce font to 9pt (small) but there is not + // small font of size 9pt in original sty. + show bibliography: set text(size: font.small) + bibliography( + bibliography-file, + ..bibliography-opts, + ) + } + } +} + +/** + * A routine for setting paragraph heading. + */ +#let paragraph(body) = { + parbreak() + [*#body*] + h(1em, weak: true) +} + +/** + * A routine for rendering external links in monospace font. + */ +#let url(uri) = { + return link(uri, raw(uri)) +}