Skip to content

Commit

Permalink
Merge pull request #2 from daskol/style/neurips2023
Browse files Browse the repository at this point in the history
Add NeurIPS 2023 template
  • Loading branch information
daskol authored Feb 22, 2024
2 parents c00d373 + d329d92 commit 34f24a1
Show file tree
Hide file tree
Showing 10 changed files with 1,149 additions and 1 deletion.
6 changes: 6 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
# Ignore LaTeX output files.
*.aux
*.bbl
*.bst
*.log
*.out
15 changes: 14 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
169 changes: 169 additions & 0 deletions colorize-annotations.py
Original file line number Diff line number Diff line change
@@ -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()
53 changes: 53 additions & 0 deletions neurips/README.md
Original file line number Diff line number Diff line change
@@ -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
Binary file added neurips/example-paper.latex.pdf
Binary file not shown.
Binary file added neurips/example-paper.typst.pdf
Binary file not shown.
18 changes: 18 additions & 0 deletions neurips/logo.typ
Original file line number Diff line number Diff line change
@@ -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$)])
})
25 changes: 25 additions & 0 deletions neurips/main.bib
Original file line number Diff line number Diff line change
@@ -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}
}
Loading

0 comments on commit 34f24a1

Please sign in to comment.