Initial Control Points

By default, fit() solves the least-squares problem exactly (solver="lstsq"), so it needs no initial control points. When you train with the Lightning trainer instead (solver="trainer", which needs the lightning extra; see Installation), fit() starts from random control points unless you supply your own via the init argument, which may help the optimizer converge faster or to a better solution when you have prior knowledge about the shape of the target manifold. With either solver, init also gives the values of the control points held fixed by freeze (see Partial Training).

The init argument accepts:

  • a BezierSimploid, e.g., a model returned by an earlier fit() or loaded from a file; its degree is then used, so degree may be omitted;

  • a dictionary mapping multi-index tuples to value vectors, where each key is a multi-index \(\mathbf{d} \in \mathbb{N}_D^M\) (e.g., (2, 0)) and each value is the corresponding control point \(\mathbf{p}_{\mathbf{d}} \in \mathbb{R}^N\);

  • a tensor of shape (n_control_points, n_values) whose rows follow indices().

import torch
import torch_bsf

ts = torch.tensor([[1.0, 0.0], [0.5, 0.5], [0.0, 1.0]])
xs = torch.tensor([[0.0, 0.1], [1.0, 1.1], [2.0, 2.1]])

init = {
   (2, 0): [0.0, 0.1],
   (1, 1): [1.0, 1.1],
   (0, 2): [2.0, 2.1],
}
bs = torch_bsf.fit(ts, xs, degree=2, init=init, solver="trainer", max_epochs=1,
                   enable_progress_bar=False, enable_model_summary=False, logger=False)
print(bs.degree)
(2,)

Saving and Loading

Save a fitted model with torch_bsf.simploidal.save() and load it with torch_bsf.simploidal.load(); the loaded model can be passed as init. The command-line interface instead accepts a file path directly via --init, whose type and degree are then used in place of --degree.

import tempfile
from pathlib import Path

from torch_bsf.simploidal import load, save

with tempfile.TemporaryDirectory() as d:
   save(Path(d) / "control_points.yml", bs)
   init = load(Path(d) / "control_points.yml")
print(init.degree)
(2,)

The file format is chosen by the extension, as described below. In every format except .pt, the key of a control point is the string representation of its index. For a Bézier simplex, it is the multi-index itself, e.g., "(2, 0)". For a Bézier simploid with several factors, it is a tuple with one multi-index per factor (see below). The degree and the dimension are recovered from the keys, and every control point must be present exactly once. Text files written by earlier versions have the same keys and can still be loaded, but .pt files of earlier versions (pickled models) cannot.

Pickled PyTorch

A dictionary with the keys "simploid_type", "degree" and "control_points" saved with torch.save(). It is loaded with weights_only=True, so loading never executes arbitrary code. See PyTorch documentation for details:

CSV

"(2, 0)",0.0,0.1
"(1, 1)",1.0,1.1
"(0, 2)",2.0,2.1

TSV

(2, 0)       0.0     0.1
(1, 1)       1.0     1.1
(0, 2)       2.0     2.1

JSON

{
  "(2, 0)": [0.0, 0.1],
  "(1, 1)": [1.0, 1.1],
  "(0, 2)": [2.0, 2.1]
}

YAML

(2, 0): [0.0, 0.1]
(1, 1): [1.0, 1.1]
(0, 2): [2.0, 2.1]

Bézier Simploids

The same formats hold Bézier simploids, with one multi-index per factor in each key, e.g., "((2, 0, 0), (1, 0))" for a prism of type \((2, 1)\). load() also accepts this nested form for a Bézier simplex, e.g., "((2, 0),)". See Simploidal Sets.