Migrating to the unified v0 API
This guide describes the unified API in documentation version 0.24.0. There are no
legacy aliases or alternate result types. Later pre-1.0 releases can add
further changes: use the version selector to read the guide matching your
installed release, and consult its changelog. The master copy documents
development toward the next release; it can include unreleased additions.
Previous API |
Replacement |
|---|---|
BezierSimploid / SimploidalBezierSpace |
BezierSpace + BezierModel |
fit(params, values, degree, …) |
fit(space, Samples(params, values), …).model |
fit_simploidal_set |
BezierSpace.on_set + Samples.from_cells + fit |
fit_sgd |
minimize(model, loss, method=PreconditionedSGD(…)) |
fit_kfold / automatic ensemble |
cross_validate / explicit Ensemble(result.models) |
solver=”trainer” and Trainer keyword arguments |
integrations.lightning.regression_module |
init / freeze for regression |
FixedControls with explicit global coefficient values |
ridge / smoothness_weight |
QuadraticPenalty(ridge=…, smoothness=…) |
implicit normalization |
explicit output transforms before fitting |
save/load chosen by extension |
io.save_model / io.load_model |
separate simplex and simploid samplers and plotters |
sampling.sample / sampling.grid / plotting.plot_model |
BezierSimploidRegressor |
sklearn.BezierRegressor |
Regression now solves in CPU float64 and returns a float64 model. Penalties
are defined relative to weighted mean squared output-vector error, not a
sample-count-dependent sum. data_mse additionally divides by output size.
Existing numerical penalty values may therefore need retuning.
The internal _kernels package retains tested geometry algorithms. It is
private and is not a compatibility API.
Skeleton allocation plans (unreleased)
optimal_skeleton_ratio now returns SkeletonAllocation
for every strategy. It no longer returns a tuple in dimension mode or a
dictionary in cell mode. Select allocation="auto" (the new default),
"cell" or "dimension" explicitly; manual modes never fall back.
from torch_bsf import BezierSpace, SimploidalSet
from torch_bsf.sampling import optimal_skeleton_ratio, sample_skeleton
domain = SimploidalSet()
_ = domain.add_simplex(1)
space = BezierSpace.on_set(domain, degree=2)
plan = optimal_skeleton_ratio(space)
assert plan.strategy == "cell"
points = sample_skeleton(space, 20, allocation=plan, seed=0)
old_dimension_ratio = optimal_skeleton_ratio(space, allocation="dimension").ratio
explicit_cell_weights = optimal_skeleton_ratio(space, allocation="cell").cell_weights
assert len(points) == 20
assert abs(sum(old_dimension_ratio) - 1) < 1e-14
assert abs(sum(explicit_cell_weights.values()) - 1) < 1e-14
Use the entire plan when sampling: passing only plan.ratio from a cell
plan discards unequal within-dimension shares. Explicit ratio and
cell_weights still work. The sampler’s default now optimizes per cell on
assembled spaces, so seeded rows may change; allocation="dimension"
retains the previous design. Bare simplex/simploid constructors directly
retain the old fast path and default draws, and report
fallback_reason="single_cell_fast_path". In the zero-coefficient rescue
case, the reason is "zero_cell_coefficients". Invalid input, numerical
precision and work-limit failures remain errors.
The coefficient API keeps its tuple/dictionary meanings and default dimension mode; its return type does not follow the automatic sampling strategy.
Python 3.11 minimum (unreleased)
The next release supports Python 3.11 through 3.15. Recreate Python 3.10
environments with a supported interpreter before upgrading pytorch-bsf.
The Python 3.10 dependency resolution selected vulnerable Pillow 12.3.0
and filelock 4.1.0. Their security fixes require Python 3.11 or later:
Pillow 13.0.0 and filelock 4.2.1. The refreshed uv.lock keeps Pillow
13.0.0 and filelock 4.2.4 for supported interpreters.
The NumPy lower bound is now 1.23.2, which provides Python 3.11 wheels.
Python 3.15 is tested with the core and the Lightning, SciPy, scikit-learn
and plotting integrations. The mlflow and all extras currently
require Python 3.11-3.14: MLflow 3.17.2 requires PyArrow below 26, while
Python 3.15 wheels start with PyArrow 26. Use the other extras on Python
3.15 until MLflow supports that dependency combination.
See the Pillow security advisory and filelock security advisory.
Separate geometry, observations and fitted state
Construct BezierSpace from your input domain, then pair parameter rows
and outputs in Samples. Regression returns a FitResult containing a
model, numerical status, metrics and diagnostics. Call the model to predict;
the result object itself is not callable.
import torch
from torch_bsf import BezierSpace, Samples, fit, QuadraticPenalty
x = torch.linspace(0, 1, 20, dtype=torch.float64)
parameters = torch.stack([1 - x, x], dim=1)
values = (x.square() + .2 * x)[:, None]
space = BezierSpace.simplex(1, degree=2)
result = fit(space, Samples(parameters, values)).require_success()
predictions = result.model(parameters)
assert predictions.shape == (20, 1)
assert result.metrics["data_mse"] < 1e-24
assert result.diagnostics.compute_dtype == "float64"
For simplex, the dimension is one less than the barycentric column
count: two columns describe a segment, three describe a triangle. For
simploid, pass factor dimensions and a matching degree tuple. For an
assembled domain, retain per-row cell IDs with Points or
Samples.from_cells. Equal local coordinates in different cells need
not describe the same location. See Single and assembled domains for the
geometric picture and coordinate conventions.
Translate penalties and constraints deliberately
Replace numeric penalty keywords with a QuadraticPenalty configuration:
regularized = fit(
space, Samples(parameters, values),
penalty=QuadraticPenalty(ridge=1e-5, smoothness=1e-4),
).require_success()
assert regularized.model(parameters).shape == values.shape
Retune these strengths under the new normalized objective rather than
assuming that the old numbers have the same effect. The regression data
term is weighted mean squared vector error; the reported data_mse is
also divided by output count. Compare predictions in original output units,
not just an old scalar loss value.
Use FixedControls for coefficient values known exactly. An old initial
guess is not an exact constraint and should not automatically become a
fixed row. Conversely, a boundary that must remain exact should not be
represented only by a large sample weight. Resolve rows with
space.control_index because coefficient ordering is a property of the
compiled space. See Fixed controls.
For an original differentiable cost, construct a BezierModel with
explicit coefficients and pass it to minimize with a typed method.
Regression fit has no iterative initial model. Lightning is an optional
adapter around the common model rather than a regression solver selector.
See Explicit initial models and Objective minimization.
Replace implicit preprocessing and model storage
Fit output transforms on training data, apply the same transform to exact fixed values, and invert predictions before reporting physical errors. Within cross-validation, learn preprocessing separately inside each fold. Generic feature standardization is inappropriate for barycentric inputs because it destroys their per-factor sum-to-one constraints.
Use the explicit archive interface for new models:
from pathlib import Path
from tempfile import TemporaryDirectory
from torch_bsf.io import save_model, load_model
with TemporaryDirectory() as directory:
path = Path(directory) / "curve.bsf"
save_model(path, result.model)
restored = load_model(path)
assert torch.equal(restored(parameters), predictions)
The archive stores compiled geometry and coefficients regardless of the filename extension. It does not imply conversion of a legacy artifact. For old models, reconstruct their space and coefficient correspondence explicitly, then compare predictions at reference points before saving in the new format. A control-point CSV alone does not reconstruct glued topology.
Validation and integration changes
cross_validate returns fold results, and an ensemble must be constructed
explicitly when that is the intended predictor. Selecting a degree does
not by itself refit on all observations; perform that final fit with the
selected configuration. The scikit-learn GridSearchCV adapter follows
its own default refit policy. See Cross-validation and degree search and
Scikit-learn integration.
Before replacing an existing workflow, compare a fixed batch of predictions,
boundary values, transformed outputs and save/load results. Inspect
success and solver diagnostics rather than treating a returned model
as sufficient evidence of convergence. Numerical differences may reflect
the new precision, objective normalization or deliberately changed penalties;
identify which configuration is being compared.