Skip to content

Typeset the math#

to_latex, to_typst and to_markdown print a spec as LaTeX, Typst or Markdown equations, from the file alone, without attaching data or running a solver.

import mathspec as ms

spec = ms.to_spec('spec.yaml')  # read and checked once, then printed three ways

print(ms.to_latex(spec))  # amsmath align
print(ms.to_typst(spec))  # compiles without a TeX toolchain
print(ms.to_markdown(spec))  # renders as-is on GitHub

Each function takes a path, the YAML, a mapping, a Spec or a Program, and prints spec.program for a spec or the Program it was handed. A program is what the file means, with every name typed. From a shell, python -m mathspec latex spec.yaml prints the same, and typst or markdown in place of latex picks the format.

Print a spec as math is the recipe, and every operator as math shows what each operator prints.

Options#

The three functions take the same keywords, and the command line spells each as a flag. The Python API gives each signature.

symbols --symbols FILE How the names print. See symbol tables. Default: derived from the names in the file
standalone --standalone Emit a document that compiles. Default: a fragment to include
legend --no-legend Print the tables of symbols and notation above the math. Default: on
numbered --no-numbers Number the equations. Default: on
inline_expressions --inline-expressions Substitute each named expression that the math reads into the equations that read it, instead of defining it once. Default: off
— --expand Print the variables and constraints the piecewise: and sos: entries state, rather than the entries. Default: off

-o FILE writes to a file instead of stdout.

  • The spec's description: opens the document.
  • The legend lists Given, Sets, Parameters, Masks, Variables, Definitions and Notation. Notation has a row for each translation or position symbol that the equations print, with the spec spelling that it stands for. The math follows under Masks, Variable domains, Definitions, Objective, Subject to and Assumptions. Under Subject to, the constraints come first, then the special ordered sets, then the curves. Each part keeps the order of the file's sections.
  • A piecewise: entry prints as one line, which quantifies the curve over the frame of the entry, because the entry states one curve at each coordinate of that frame. To print its rows, print spec.expand() or pass --expand (see an expansion).
  • An assumptions: entry prints under an Assumptions heading, last, beside what each curve assumes of its breakpoints. A spec that assumes nothing of its data prints no such heading.
  • A named expression prints its symbol where it is used and its body once, under a Definitions heading, in file order. A cased expression, a reported entry and a term keep their definition line under either inline_expressions setting.
  • A mask prints its upright symbol where a file reads it, and its predicate once, under a Masks heading, with ⟺. It prints as its symbol under either inline_expressions setting.
  • Wherever the math moves an index, which every shift does, the document prints a line saying what that notation means.
  • A file that does not load does not print.
  • The typesetter does not break lines, so a wide equation runs off the page.

Markdown's delimiters#

to_markdown prints math between the two pairs GitHub and GitLab read verbatim: $`…`$ inline, and a ```math fence for a block. It never prints $…$ or $$…$$. For a renderer that reads $…$ alone, print with to_latex and write that renderer's delimiters around the result.

Descriptions#

A description: prints as plain text, with one exception: a name in backticks, such as `capital_cost`, prints in monospace in every output format. The typesetter escapes every other character that the output format would read as markup. The legend prints the description of every dimension, parameter and variable.

Printing one entry on its own#

typeset_line returns the line that the document prints for one named expression, mask, constraint, special ordered set, curve, assumption or variable. The line has its quantifier, but no document, label, number or math delimiters:

ms.typeset_line('spec.yaml', 'spend', 'latex')
# \mathit{spend}_{t} = \sum_{g \in \mathcal{G}} \mathit{dispatch}_{t,g} \cdot \mathrm{cost}_{g} \qquad \forall\, t \in \mathcal{T}
ms.typeset_line('spec.yaml', 'balance', 'latex')
# \sum_{g \in \mathcal{G}} \mathit{dispatch}_{t,g} = \mathrm{load}_{t} \qquad \forall\, t \in \mathcal{T}

It takes what the other functions take, plus the name, the format and an optional symbols table. A Markdown line arrives without delimiters too, so put it inside the inline pair:

line = ms.typeset_line('spec.yaml', 'balance', 'markdown')
print(f'The balance holds: $`{line}`$')

A single line has no Definitions section beside it, so typeset_line substitutes each plain named expression that the line uses. A cased expression prints as its symbol, and a second call with its name prints its definition.

typeset_line refuses a name that is none of the seven kinds, and names the closest match. It also refuses a name declared as two of them, such as a constraint and a variable.

Symbol tables#

With no table, the symbols are derived from the names in the file, such as \(\mathrm{load}_t\) and \(\mathrm{capacity}_g\). A symbol table replaces them with the symbols you choose, such as \(\ell\) for load. Pass a path to a YAML file, the same keys as a dict, or a ms.SymbolTable:

# dispatch.symbols.yaml
notation: latex
dimensions:
  snapshot: { index: s, set: "\\mathcal{S}" }
  generator: { index: g, set: "\\mathcal{G}" }
names:
  cost: c
  load: "\\ell"
  capacity: "\\bar p"
ms.to_latex('dispatch.yaml', symbols='dispatch.symbols.yaml')
Section
notation Required. latex or typst: the language the entries are written in
dimensions For each dimension, an index letter and a set symbol. Either may be omitted
names For each parameter, variable or named expression, its symbol

The typesetter prints each entry as you wrote it and does not translate between notations, so to_typst refuses a table with notation: latex. A key that names nothing in the spec, and nothing that its piecewise: or sos: entries expand into, is an error that names the closest match.

Nothing in a symbol table changes what the file means.