Python API
This reference follows a model through geometry construction, numerical fitting, evaluation and optional integrations. The public types are shared across these operations. For runnable end-to-end examples and numerical interpretation, start with Quickstart and the Advanced Topics guides.
core
BezierSpace defines the domain and global control layout. Points
carries coordinates and cell IDs, Samples adds observed values and
weights, and BezierModel adds output coefficients to a space. Geometry
does not depend on output count. Use these types to make data alignment
and model state explicit.
Spaces, observations and differentiable models shared by every fitting backend.
- class torch_bsf.core.BasisOperator(space: BezierSpace, points: Points)[source]
Bases:
objectCell-local basis supporting products, transpose products and explicit export.
- matmat(coefficients: Tensor) Tensor[source]
Evaluate a coefficient matrix, accumulating only local contributions.
- class torch_bsf.core.BezierModel(space: BezierSpace, coefficients: Any)[source]
Bases:
ModuleA vector-valued differentiable map on a compiled
BezierSpace.- Parameters:
space – Geometry, degrees and coefficient ordering.
coefficients – Matrix of shape
(space.n_controls, n_outputs). Copied on construction.
Examples
>>> import torch >>> from torch_bsf import BezierSpace, BezierModel >>> model = BezierModel(BezierSpace.simplex(1, degree=1), torch.tensor([[1.], [3.]])) >>> model(torch.tensor([[0.5, 0.5]])).item() 2.0
- copy() BezierModel[source]
Copy coefficients without mutating or duplicating immutable geometry.
- class torch_bsf.core.BezierSpace(cells: tuple[~torch_bsf.core.Cell, ...], n_controls: int, main_cells: tuple[int, ...], _edges: tuple[tuple[int, int], ...], _source_ids: ~collections.abc.Mapping[~torch_bsf.geometry.Simploid, int] = <factory>, _topology: tuple[~typing.Any, ...] = ())[source]
Bases:
objectImmutable polynomial space, independent of learned values and output dimension.
Construct a simplex with
simplex(), a product withsimploid(), or compile a mutable topology builder withon_set().Examples
>>> from torch_bsf import BezierSpace >>> space = BezierSpace.simplex(dimension=2, degree=3) >>> space.n_controls 10
- basis(points: Points | Tensor) BasisOperator[source]
Build a cell-local linear operator without a global dense allocation.
- cell_id(cell: int | Simploid) int[source]
Resolve an integer or an original builder handle to its frozen cell ID.
- compatible(other: BezierSpace) bool[source]
Whether coefficient ordering and all addressable cells agree.
- control_index(index: Sequence[Any], *, cell: int | Simploid = 0) int[source]
Resolve a cell-local multi-index to a globally shared coefficient row.
- classmethod on_set(domain: SimploidalSet, *, degree: int | Mapping[Simploid, int | Sequence[int]], default_degree: int | None = None) BezierSpace[source]
Snapshot a simploidal set, propagating compatible degrees across shared faces.
The builder’s cell handles remain usable for addressing this snapshot; subsequent changes to the builder cannot change its basis or layout.
- classmethod simplex(dimension: int, *, degree: int) BezierSpace[source]
Create a single simplex, including a zero-dimensional constant.
- classmethod simploid(dimensions: Sequence[int], *, degree: int | Sequence[int]) BezierSpace[source]
Create a tensor product of simplices with one degree per factor.
- class torch_bsf.core.Cell(dimensions: tuple[int, ...], degree: tuple[int, ...], controls: tuple[int, ...])[source]
Bases:
objectA compiled cell’s factor dimensions, degrees and global coefficient rows.
- class torch_bsf.core.FixedControls(values: Mapping[int, Any])[source]
Bases:
objectExplicit fixed values keyed by globally shared coefficient row.
- class torch_bsf.core.Points(parameters: Any, cell_ids: Any = None)[source]
Bases:
objectBarycentric coordinate rows and their cell IDs, in prediction order.
- class torch_bsf.core.QuadraticPenalty(ridge: float = 0.0, smoothness: float = 0.0)[source]
Bases:
objectWeights of squared coefficients and adjacent coefficient differences.
- class torch_bsf.core.Samples(parameters: Any, values: Any, *, cell_ids: Any = None, weights: Any = None)[source]
Bases:
objectPoints with finite observed outputs and optional nonnegative weights.
geometry
Build an assembled domain with SimploidalSet and identify the faces
that must share coefficients. Compile it into BezierSpace before
fitting. Builder mutations do not alter an existing compiled space.
See Single and assembled domains for chart orientation and continuity.
Topology builders for simplex products and their glued parameter domains.
Compile a builder with BezierSpace.on_set before fitting. A compiled space
snapshots the topology; subsequent builder mutations cannot change its basis.
- class torch_bsf.geometry.Simploid(simploid_type: Sequence[int])[source]
Bases:
objectA simploid of a
SimploidalSet.Simploids are created by the construction methods of
SimploidalSet, not directly. A non-degenerate simploid stores its faces explicitly. A degenerate simploid is stored in canonical form, as a non-degenerate base simploid together with aDegeneracyMap, so that two equal degenerate simploids are the same object.Examples
>>> from torch_bsf.geometry import SimploidalSet >>> S = SimploidalSet() >>> prism = S.add_simploid((2, 1)) >>> prism.simploid_type, prism.dim ((2, 1), 3) >>> prism.face(1, 0).simploid_type # a triangle (2,) >>> prism.face(0, 0).simploid_type # a square (1, 1) >>> e = prism.face(1, 0).face(0, 0) # an edge >>> e.degeneracy(0, 0).is_degenerate True >>> e.degeneracy(0, 0).face(0, 0) is e True
- property base: Simploid[source]
The non-degenerate simploid this one is degenerate from, or itself if it is not degenerate.
- degeneracy(i: int, j: int) Simploid[source]
The degenerate simploid \(s^{i+1}_j\) (
j >= 0) or \(s^i_{-1}\) (j == -1).- Parameters:
i – The factor index:
0 <= i < lengthifj >= 0,0 <= i <= lengthifj == -1.j – The vertex of factor
ito repeat, or-1to insert a degenerate edge as the new factori.
- Return type:
The degenerate simploid.
- property degeneracy_map: tuple[tuple[int, tuple[int, ...]], ...][source]
The degeneracy map from
baseto this simploid (the identity map if not degenerate).
- face(i: int, j: int) Simploid[source]
The face \(d^{i+1}_j\): removes vertex
jof factori.- Parameters:
i – The factor index,
0 <= i < length.j – The vertex index,
0 <= j <= a_i.
- Return type:
The face.
- class torch_bsf.geometry.SimploidalSet[source]
Bases:
objectA simploidal set [PL18].
A simploidal set is a set of simploids equipped with face and degeneracy operators satisfying the commutation relations of Definition 1 of the paper. Only non-degenerate simploids are stored; degenerate simploids are created on demand by
Simploid.degeneracy()and by the faces of degenerate simploids.Simploidal sets are built with the basic operations of the paper:
add_vertex(),add_simplex(),add_simploid(): complete cells with their boundary;product(): cartesian product of two simploidal sets;identify(): identification of two simploids (and of their boundaries);degenerate(): degeneracy of a simploid onto a degenerate simploid of one of its faces;cone(): cone with a new vertex.
Examples
A tetrahedron and a cube connected by a prism (Figure 1(c) of the paper):
>>> from torch_bsf.geometry import SimploidalSet >>> S = SimploidalSet() >>> tetra = S.add_simplex(3) >>> prism = S.add_simploid((2, 1)) >>> cube = S.add_simploid((1, 1, 1)) >>> _ = S.identify(tetra.face(0, 0), prism.face(1, 0)) >>> _ = S.identify(prism.face(0, 0), cube.face(0, 0)) >>> S.check() >>> [s.simploid_type for s in S.main_simploids()] [(3,), (2, 1), (1, 1, 1)] >>> len(S.simploids(0)) # 4 + 6 + 8 vertices, minus 3 and 4 identified ones 11
- add_simplex(dim: int) Simploid[source]
Adds a complete simplex (a simploid of type \((\mathrm{dim})\)) with its boundary.
Face
face(0, j)of a simplex \((v_0, \ldots, v_k)\) is \((v_0, \ldots, \hat v_j, \ldots, v_k)\).- Parameters:
dim – The dimension of the simplex.
- Return type:
The simplex.
Examples
>>> from torch_bsf.geometry import SimploidalSet >>> S = SimploidalSet() >>> tri = S.add_simplex(2) >>> len(S), len(tri.vertices()) (7, 3)
- add_simploid(simploid_type: Sequence[int]) Simploid[source]
Adds a complete simploid of the given type with its boundary.
It is built as the cartesian product of complete simplices.
- Parameters:
simploid_type – The type \((a_1, \ldots, a_n)\).
- Return type:
The simploid.
Examples
>>> from torch_bsf.geometry import SimploidalSet >>> S = SimploidalSet() >>> square = S.add_simploid((1, 1)) >>> len(S.simploids(0)), len(S.simploids(1)), len(S.simploids(2)) (4, 4, 1)
- betti_numbers(field: Literal['Q', 'Z2'] = 'Q') list[int][source]
The Betti numbers \(\beta_0, \ldots, \beta_{\mathrm{dim}}\) of the simploidal set.
They are the dimensions of the homology groups of the normalized chain complex (see
boundary_matrix()), \(\beta_k = \dim C_k - \operatorname{rank} \partial_k - \operatorname{rank} \partial_{k+1}\). The ranks are computed exactly by integer Gaussian elimination, so the result does not depend on a floating-point tolerance.- Parameters:
field – The coefficient field:
"Q"(rational numbers) or"Z2"(integers modulo 2). They differ only when the integral homology has torsion, e.g., for non-orientable spaces.- Return type:
The Betti numbers, one per dimension from
0todim(empty if the set is empty).- Raises:
ValueError – If
fieldis neither"Q"nor"Z2".
Examples
A torus, a square whose opposite edges are identified:
>>> from torch_bsf.geometry import SimploidalSet >>> S = SimploidalSet() >>> square = S.add_simploid((1, 1)) >>> _ = S.identify(square.face(0, 0), square.face(0, 1)) >>> _ = S.identify(square.face(1, 0), square.face(1, 1)) >>> S.betti_numbers() [1, 2, 1]
- boundary_matrix(dim: int) Tensor[source]
The matrix of the boundary operator \(\partial_{\mathrm{dim}}\) of the normalized chain complex.
Following Section 4.4 of Peltier and Lienhardt [PL18], the chain group \(C_k\) is spanned by the non-degenerate simploids of dimension \(k\), and the boundary of a simploid \(\sigma\) of type \((a_1, \ldots, a_n)\) is
\[\partial \sigma = \sum_{i=1}^n \sum_{j=0}^{a_i} (-1)^{a_1 + \cdots + a_{i-1} + j} d^i_j \sigma,\]the Koszul sign rule for the product \(\Delta^{a_1} \times \cdots \times \Delta^{a_n}\), where the terms whose face \(d^i_j \sigma\) is degenerate are dropped.
- Parameters:
dim – The dimension \(k \ge 0\) of the simploids whose boundary is taken.
- Returns:
An integer (
torch.long) matrix of shape(len(simploids(dim - 1)), len(simploids(dim)))column
cholds the boundary ofsimploids(dim)[c]in the basissimploids(dim - 1).boundary_matrix(0)has no rows.
- Raises:
ValueError – If
dimis negative or a face does not belong to this set.
Examples
>>> from torch_bsf.geometry import SimploidalSet >>> S = SimploidalSet() >>> square = S.add_simploid((1, 1)) >>> [[key for key, f in square.faces() if f is e] for e in S.simploids(1)] [[(0, 1)], [(0, 0)], [(1, 1)], [(1, 0)]] >>> S.boundary_matrix(2) tensor([[-1], [ 1], [ 1], [-1]]) >>> bool((S.boundary_matrix(1) @ S.boundary_matrix(2) == 0).all()) True
- check() None[source]
Checks the face commutation relations (II) of Definition 1 for every simploid.
- Raises:
ValueError – If a relation does not hold or a face does not belong to this set.
- cone(simploids: Sequence[Simploid] | None = None) tuple[Simploid, dict[Simploid, Simploid]][source]
Cones simploids with a new vertex (Algorithm 8 of the paper).
The cone of a simploid of type \((a_1, \ldots, a_k)\) is a simploid of type \((a_1, \ldots, a_k, 1)\): an extrusion whose far end is degenerated into the new vertex
v. For example, the cone of a square is a square-based pyramid of type \((1, 1, 1)\). The conecofsigmasatisfiesc.face(k, 1) is sigmaandc.face(k, 0)is the degeneracy ofvwith the type ofsigma, wherek = sigma.length.- Parameters:
simploids – The simploids to cone (their boundaries are coned too). All simploids if
None.- Returns:
vertex – The new vertex.
cones – Maps each coned non-degenerate simploid to its cone.
Examples
>>> from torch_bsf.geometry import SimploidalSet >>> S = SimploidalSet() >>> square = S.add_simploid((1, 1)) >>> v, cones = S.cone([square]) >>> pyramid = cones[square] >>> pyramid.simploid_type (1, 1, 1) >>> pyramid.face(2, 0).is_degenerate, pyramid.face(2, 0).base is v (True, True)
- degenerate(simploid: Simploid, face: Simploid, i: int, j: int) Simploid[source]
Degenerates a simploid onto a degenerate simploid of one of its faces.
This identifies
simploidwithface.degeneracy(i, j); the two must have the same type, and their boundaries are identified accordingly.- Parameters:
simploid – The simploid to degenerate.
face – The simploid it degenerates into.
i – The degeneracy operator, as in
Simploid.degeneracy().j – The degeneracy operator, as in
Simploid.degeneracy().
- Return type:
The resulting degenerate simploid.
Examples
Collapse an edge of a square into a vertex, which turns the square into a triangle-shaped simploid of type
(1, 1):>>> from torch_bsf.geometry import SimploidalSet >>> S = SimploidalSet() >>> square = S.add_simploid((1, 1)) >>> edge = square.face(0, 0) # an edge of type (1,) >>> d = S.degenerate(edge, edge.face(0, 0), 0, -1) >>> d.is_degenerate, square.face(0, 0) is d (True, True) >>> len(S.simploids(0)) 3
- identify(a: Simploid, b: Simploid) Simploid[source]
Identifies two simploids of the same type.
This is the higher-level identification of the paper: the boundaries of
aandbare identified face by face (a.face(i, j)withb.face(i, j)), and the degenerate simploids induced by the merged simploids are identified too. If one of the identified simploids is degenerate, the result is degenerate (this is howdegenerate()works).References to simploids that are merged away become stale; use the returned simploid and the faces of the surviving simploids instead.
- Parameters:
a – The simploids to identify.
b – The simploids to identify.
- Return type:
The simploid resulting from the identification.
- Raises:
ValueError – If the simploids (or faces to be identified) have different types, do not belong to this set, or if two distinct degenerate simploids would be identified.
Examples
Glue two triangles along an edge:
>>> from torch_bsf.geometry import SimploidalSet >>> S = SimploidalSet() >>> t1, t2 = S.add_simplex(2), S.add_simplex(2) >>> e = S.identify(t1.face(0, 0), t2.face(0, 2)) >>> t1.face(0, 0) is e and t2.face(0, 2) is e True >>> len(S.simploids(0)), len(S.simploids(1)) (4, 5)
- main_simploids() list[Simploid][source]
The non-degenerate simploids that are not a face of another non-degenerate simploid.
- product(other: SimploidalSet) tuple[SimploidalSet, dict[tuple[Simploid, Simploid], Simploid]][source]
The cartesian product of two simploidal sets (Algorithm 7 of the paper).
The product of simploids of types \((a_1, \ldots, a_k)\) and \((b_1, \ldots, b_l)\) is a simploid of type \((a_1, \ldots, a_k, b_1, \ldots, b_l)\).
- Parameters:
other – The second factor.
- Returns:
product – A new simploidal set.
pairs – Maps each pair
(sigma1, sigma2)of non-degenerate simploids tosigma1 x sigma2.
Examples
>>> from torch_bsf.geometry import SimploidalSet >>> A, B = SimploidalSet(), SimploidalSet() >>> tri, edge = A.add_simplex(2), B.add_simplex(1) >>> P, pairs = A.product(B) >>> pairs[(tri, edge)].simploid_type (2, 1)
interpolation
SimplicialMesh compiles user-supplied simplices into a cubic
BezierSpace whose shared faces share controls, and locates points in it.
hermite_interpolant returns a continuous piecewise cubic BezierModel
through vertex data that, when clamped, stays within the data range of each
simplex. See Shape-preserving interpolation on simplicial meshes for the construction and its
error bound.
Shape-preserving cubic Bézier interpolation on simplicial complexes (“simplicial PCHIP”).
A SimplicialMesh glues user-supplied simplices of \(\mathbb{R}^d\) into one
BezierSpace, so that simplices sharing a face share the controls on it.
hermite_interpolant() fills that space with a cubic Bézier model that interpolates vertex data:
a vertex control is the datum \(f_i\);
the control on edge \(i \to j\) next to vertex \(i\) is \(f_i + g_i \cdot (x_j - x_i) / 3\), with a vertex gradient \(g_i\);
the control at the centre of face \((i, j, l)\) is the sum of the six edge controls of the face divided by four, minus \((f_i + f_j + f_l) / 6\), which reproduces quadratic data exactly.
Every control depends only on the data of the edge or face it lies on, so the interpolant is
continuous across shared faces. With clamp=True every edge (face) control is clipped into the
range of the data at the vertices of that edge (face). By the convex-hull property, the prediction
on each simplex then stays within the range of its vertex data, which bounds the sup error by the
modulus of continuity of the data at the mesh size. In one dimension this is the range limiter of
piecewise cubic Hermite interpolation (PCHIP) [FC80].
- class torch_bsf.interpolation.SimplicialMesh(vertices: Any, simplices: Any)[source]
Bases:
objectA conforming simplicial complex in \(\mathbb{R}^d\) compiled into a cubic
BezierSpace.- Parameters:
vertices – Vertex coordinates of shape
(n_vertices, d),d >= 1.simplices – Vertex numbers of shape
(n_simplices, d + 1). The simplices must be non-degenerate and meet face to face (as in a Delaunay triangulation); the mesh is not generated here.
- simplices[source]
Each simplex with its vertex numbers sorted in ascending order. The barycentric coordinates of cell
k(as returned bylocate()) refer to the verticessimplices[k]in this order.
- space[source]
The compiled degree-3 space, in which simplices sharing a face share the controls on it.
Examples
Two triangles sharing the diagonal of the unit square:
>>> import torch >>> from torch_bsf.interpolation import SimplicialMesh >>> mesh = SimplicialMesh([[0.0, 0.0], [1.0, 0.0], [0.0, 1.0], [1.0, 1.0]], [[0, 1, 2], [3, 2, 1]]) >>> mesh.simplices.tolist() [[0, 1, 2], [1, 2, 3]] >>> points = mesh.locate(torch.tensor([[0.25, 0.25], [0.75, 0.75]])) >>> points.cell_ids.tolist() == mesh.cell_ids.tolist() True >>> points.parameters[0].tolist() [0.5, 0.25, 0.25]
- barycentric(x: Any) Tensor[source]
Barycentric coordinates of points with respect to every simplex.
- Parameters:
x – Points of shape
(n_points, d).- Returns:
Coordinates of shape
(n_points, n_simplices, d + 1), in the vertex order ofsimplices. They sum to one and are all nonnegative exactly when the point lies in the simplex.- Return type:
- locate(x: Any, *, tol: float = 1e-08, batch_size: int = 1024) Points[source]
Find the simplex containing each point and its barycentric coordinates there.
Every simplex is tested (brute force). A point on a shared face is assigned to one of the simplices containing it; the prediction does not depend on that choice because the interpolant is continuous.
- Parameters:
x – Points of shape
(n_points, d).tol – Points whose barycentric coordinates are at least
-tolin some simplex are accepted. Their coordinates are clipped to be nonnegative and renormalized, which moves points just outside the mesh onto its boundary.batch_size – Number of points tested at a time, which bounds the memory to
batch_size * n_simplices * (d + 1).
- Returns:
Barycentric coordinates and cell IDs in
space, in the order ofx.- Return type:
- Raises:
ValueError – If a point lies outside the mesh by more than
tol.
- torch_bsf.interpolation.estimate_gradients(mesh: SimplicialMesh, values: Any, *, max_rings: int = 4) Tensor[source]
Estimate the gradient of vertex data at every vertex from its mesh neighbours.
At each vertex, a quadratic polynomial is fitted by least squares with weights \(1 / |x_w - x_v|^2\) to the data on the smallest \(k\)-ring neighbourhood (\(2 \le k \le\)
max_rings) that contains at least two more points than the quadratic has unknowns. If no such neighbourhood exists, a linear polynomial is fitted on the one-ring instead. The quadratic fit reproduces the gradient of quadratic data exactly, and either fit reproduces that of linear data. Vertices that belong to no simplex get a zero gradient.- Parameters:
mesh – The mesh.
values – Vertex data of shape
(n_vertices, n_outputs)or(n_vertices,).max_rings – The largest neighbourhood to try.
- Returns:
Float64 gradients of shape
(n_vertices, d, n_outputs).- Return type:
Examples
>>> import torch >>> from torch_bsf.interpolation import SimplicialMesh, estimate_gradients >>> x = torch.linspace(0, 1, 6)[:, None] >>> mesh = SimplicialMesh(x, [[k, k + 1] for k in range(5)]) >>> g = estimate_gradients(mesh, x.square()) >>> torch.allclose(g[:, 0, 0], 2 * x[:, 0].double()) True
- torch_bsf.interpolation.hermite_interpolant(mesh: SimplicialMesh, values: Any, *, gradients: Any = None, clamp: bool = True) BezierModel[source]
Interpolate vertex data by a continuous piecewise cubic Bézier model on a simplicial mesh.
The model reproduces the data at the vertices. Without clamping, it reproduces quadratic data exactly when the gradients do (for example, with enough neighbours for
estimate_gradients()). Withclamp=True, on every simplex and for every output, the prediction lies between the smallest and largest data at the vertices of that simplex; in one dimension, monotone data then give a monotone interpolant.- Parameters:
mesh – The mesh.
values – Vertex data of shape
(n_vertices, n_outputs)or(n_vertices,).gradients – Vertex gradients of shape
(n_vertices, d, n_outputs). Estimated withestimate_gradients()ifNone.clamp – Whether to clip edge and face controls into the range of their vertex data (shape preservation).
- Returns:
A float64 model on
mesh.space. Evaluate it atmesh.locate(x).- Return type:
Examples
>>> import torch >>> from torch_bsf.interpolation import SimplicialMesh, hermite_interpolant >>> x = torch.tensor([[0.0], [1.0], [2.0], [3.0]]) >>> mesh = SimplicialMesh(x, [[0, 1], [1, 2], [2, 3]]) >>> model = hermite_interpolant(mesh, torch.tensor([0.0, 0.0, 1.0, 1.0])) >>> y = model(mesh.locate(torch.linspace(0, 3, 31)[:, None])) >>> bool((y.diff(dim=0) >= 0).all() and y.min() >= 0 and y.max() <= 1) True
- torch_bsf.interpolation.refinement_indicator(mesh: SimplicialMesh, values: Any, *, gradients: Any = None, clamp: bool = True) Tensor[source]
Score simplices for refinement by the cubic correction to linear interpolation.
The score of a simplex is its volume times the mean over outputs of the absolute difference between
hermite_interpolant()and linear interpolation at its centroid. It is large where the data bend within a large simplex. Refine the highest-scoring simplices first, for example by evaluating the response at the midpoint of their longest edge and remeshing (LongestEdgeintorch_bsf.splittingselects that edge on a single cell).- Parameters:
mesh – The mesh.
values – Vertex data of shape
(n_vertices, n_outputs)or(n_vertices,).gradients – Vertex gradients, as in
hermite_interpolant().clamp – Whether the cubic interpolant is clamped, as in
hermite_interpolant().
- Returns:
Nonnegative float64 scores of shape
(n_simplices,).- Return type:
Examples
>>> import torch >>> from torch_bsf.interpolation import SimplicialMesh, refinement_indicator >>> x = torch.arange(6.0)[:, None] >>> mesh = SimplicialMesh(x, [[k, k + 1] for k in range(5)]) >>> [round(s, 6) for s in refinement_indicator(mesh, x.square()).tolist()] [0.25, 0.25, 0.25, 0.25, 0.25]
fitting
fit solves regression on known parameter-response pairs and returns
FitResult. Fixed controls and quadratic penalties participate in the
same solve. Evaluate result.model(points) after checking the numerical
status; there is no initial guess for this linear least-squares operation.
One supervised fitting entry point for every space and numerical solver.
- torch_bsf.fitting.fit(space: BezierSpace, samples: Samples, *, solver: DenseLeastSquares | SparseLeastSquares = DenseLeastSquares(rcond=None, diagnostics='basic'), penalty: QuadraticPenalty = QuadraticPenalty(ridge=0.0, smoothness=0.0), fixed: FixedControls | None = None) FitResult[source]
Fit a space to observations, returning an explicit numerical result.
Minimizes
sum(w * squared_output_error) / sum(w)plusridge * ||P||²andsmoothness * ||D P||²in the original output units. Data MSE divides the data term by the output dimension. Fixed coefficients are exact values, not initial guesses. Neither the space nor observations are modified.- Parameters:
space – A compiled single-cell or glued-cell polynomial space.
samples – Coordinates, outputs and optional sample weights.
solver – Typed dense SVD or sparse LSMR configuration. Both compute on CPU float64 and return a float64 model; no implicit downcast is performed.
penalty – Quadratic weights, independent of the number of observations.
fixed – Explicit globally shared coefficient values.
- Returns:
Model, metrics, diagnostics and termination state. This type never changes with diagnostic settings. Predict with
result.model(...). Numerical failures returnsuccess=Falseand emit ConvergenceWarning;require_success()raises with the result attached. If no finite iterate exists,diagnostics.solution_availableis false and the model contains placeholder free coefficients rather than a fit.- Return type:
Examples
>>> import torch >>> from torch_bsf import BezierSpace, Samples, fit >>> t = torch.eye(2) >>> result = fit(BezierSpace.simplex(1, degree=1), Samples(t, [[1.], [2.]])) >>> result.success, result.model(t).tolist() (True, [[1.0], [2.0]])
point_cloud
fit_point_cloud fits observations whose parameter coordinates are
unknown, alternating inferred coordinates and controls. fit_skeleton
fits faces inductively while preserving their boundaries. These nonlinear
methods return explicit local convergence states. See
Fitting from values alone for assumptions, initialization and examples.
Value-only Bézier simplex fitting (Kobayashi et al., AAAI 2019).
Algorithm 2 alternates constrained Newton parameter updates and linear least squares. Algorithm 3 fits faces in increasing support size, preserving the controls already estimated on their boundaries.
- class torch_bsf.point_cloud.PointCloudFitResult(model: BezierModel, parameters: Tensor, objective_history: tuple[float, ...], success: bool, status: str, message: str, n_iterations: int, parameter_converged: bool, control_result: FitResult | None)[source]
Bases:
objectA fitted surface and inferred barycentric rows in observation order.
objective_historycontains the initial mean squared Euclidean distance and each accepted alternating update (no division by output dimension). Success describes local numerical convergence, not global optimality or identification of a unique parameterization. All tensors are CPU float64.- require_success() PointCloudFitResult[source]
Raise with the termination message if convergence was not established.
- class torch_bsf.point_cloud.SkeletonFitResult(model: BezierModel, faces: dict[tuple[int, ...], PointCloudFitResult], success: bool, status: str, message: str)[source]
Bases:
objectAssembled model and local results keyed by sorted zero-based vertex IDs.
Each face result contains local barycentric coordinates for that face’s observations. Previously fitted boundary controls remain exactly fixed.
- require_success() SkeletonFitResult[source]
Raise if any face failed to converge.
- torch_bsf.point_cloud.fit_point_cloud(space: BezierSpace, values: Any, *, initial: BezierModel | None = None, fixed: FixedControls | None = None, max_iter: int = 100, parameter_max_iter: int = 100, tolerance: float = 1e-05, parameter_tolerance: float = 1e-05) PointCloudFitResult[source]
Fit values without supplied domain coordinates (paper Algorithm 2).
Minimize the mean squared Euclidean distance by alternating parameter and control updates. Parameters use analytic first/second derivatives with projected Newton steps, backtracking and a gradient fallback. Controls use CPU float64 SVD least squares, never normal equations.
fixedspecifies exact controls throughout, including before the first parameter update.Automatic initialization uses the observed minimum of each output as a vertex and the affine grid between those vertices, as in the paper. It assumes minimization objectives and one output per vertex. Supply an
initialmodel for arbitrary embeddings or a different orientation. No input model or observation is modified. Only single simplices are supported. Exhausting either iteration budget does not certify convergence.tolerancebounds absolute change in root mean squared distance;parameter_tolerancebounds the projected gradient residual. The stopping rule uses the absolute change, correcting the signed-change expression printed in the paper. The objective uses the original units; scale observations explicitly when outputs have incompatible units.
- torch_bsf.point_cloud.fit_skeleton(space: BezierSpace, values: Any, *, face_values: Mapping[tuple[int, ...], Any] | None = None, initial: BezierModel | None = None, max_iter: int = 100, parameter_max_iter: int = 100, tolerance: float = 1e-05, parameter_tolerance: float = 1e-05) SkeletonFitResult[source]
Fit faces inductively while fixing their boundaries (paper Algorithm 3).
Faces are sorted tuples of zero-based vertex IDs. Only support sizes up to
min(degree, number_of_vertices)are needed. By default each face’s samples are the nondominated points for its objective subset (all outputs are minimization objectives). This requires one output per vertex.face_valuesinstead supplies every required face’s value-only samples, allowing arbitrary embeddings withinitial. Full original output vectors must be retained on every face. No face memberships or coordinates are inferred from an arbitrary embedding.Vertex controls are sample means. Higher faces use
fit_point_cloudwith all lower-face controls fixed exactly. Inspectfacesfor local stopping evidence; failure on a boundary stops assembly and returns the finite partial model withsuccess=False.
results
Results separate a computed model from evidence about the computation.
require_success raises on unsuccessful termination. Data diagnostics
describe observation identifiability, whereas system diagnostics can also
include penalty rows. Successful termination is distinct from held-out
prediction accuracy.
Explicit numerical results: a computed model is not a convergence certificate.
- exception torch_bsf.results.ConvergenceError(result: FitResult)[source]
Bases:
RuntimeErrorFailure requested by
require_success(), retaining the numerical result.
- exception torch_bsf.results.ConvergenceWarning[source]
Bases:
UserWarningA numerical method stopped without meeting its convergence criteria.
- class torch_bsf.results.FitDiagnostics(solver: str, n_samples: int, n_free_controls: int, weight_sum: float, data: MatrixDiagnostics, system: MatrixDiagnostics, compute_dtype: str = 'float64', compute_device: str = 'cpu', model_dtype: str = 'float64', model_device: str = 'cpu', iterations: tuple[int | None, ...] = (), stop_codes: tuple[int | None, ...] = (), residual_norms: tuple[float | None, ...] = (), normal_residual_norms: tuple[float | None, ...] = (), condition_estimates: tuple[float | None, ...] = (), driver: str | None = None, output_success: tuple[bool, ...] = (), output_statuses: tuple[str, ...] = (), solution_available: bool = True)[source]
Bases:
objectCompute provenance and observation/system diagnostics in original units.
driveridentifies the executed backend (gelsdorlsmr), or isNoneif no solve was executed.output_successandoutput_statusesrecord termination for each output column, in prediction-column order.solution_availableis false when a numerical failure left no finite iterate for at least one column; seeFitResultfor its placeholder. Residual norms and condition estimates from LSMR concern the augmented weighted, constrained system, not the unpenalized prediction MSE. Iterative fields useNonefor a column whose RHS overflowed and was not solved, or empty tuples if no iterative solve ran at all. Overall success can still be false after successful solves if objective evaluation or optional diagnostics fail; these failures do not erase solver termination.
- class torch_bsf.results.FitResult(model: BezierModel, metrics: dict[str, float], diagnostics: FitDiagnostics, success: bool, status: str, message: str)[source]
Bases:
objectA model, objective metrics, diagnostics and an explicit termination state.
Results are deliberately not callable. Predict with
result.model(points). Ifdiagnostics.solution_availableis false, the model is only a finite placeholder preserving fixed controls, with free rows of unavailable output columns zero. It must not be used as a fitted solution. Finite unconverged LSMR iterates are retained.- require_success() FitResult[source]
Return this result, or raise a
ConvergenceErrorcontaining it.
- class torch_bsf.results.MatrixDiagnostics(shape: tuple[int, int], rank: int | None = None, rank_threshold: float | None = None, singular_values: Tensor | None = None, condition_number: float | None = None, method: Literal['svd', 'estimate', 'not_computed', 'no_free_controls'] = 'not_computed')[source]
Bases:
objectInformation about one explicitly identified least-squares matrix.
Nonemeans not computed, never zero.methoddistinguishes a direct singular-value calculation from an iterative condition estimate. A rank below the number of free columns has infinite coefficient condition number.
solvers
Choose a typed dense or sparse least-squares configuration. Both compute CPU float64 models; sparse solving uses the optional SciPy dependency. Tolerances and iteration limits concern the numerical solve rather than polynomial capacity. See Solvers and optional execution.
Typed numerical solver configurations; importing this module does not load SciPy.
- class torch_bsf.solvers.DenseLeastSquares(rcond: float | None = None, diagnostics: Literal['basic', 'full'] = 'basic')[source]
Bases:
objectCPU float64 SVD solve of the augmented problem, without normal equations.
- Parameters:
rcond (float | None) – Relative singular-value cutoff.
Noneuses machine epsilon times the larger matrix dimension. The actual absolute threshold is recorded.diagnostics (Literal['basic', 'full']) –
basicreuses the system SVD;fulladditionally diagnoses the weighted observation matrix when regularization changes the system.
- class torch_bsf.solvers.SparseLeastSquares(atol: float = 1e-08, btol: float = 1e-08, conlim: float = 100000000.0, max_iter: int | None = None)[source]
Bases:
objectOptional SciPy LSMR on CPU float64, with explicit per-output stop codes.
- Parameters:
atol – Backward-error stopping tolerances.
btol – Backward-error stopping tolerances.
conlim (float) – Estimated condition-number limit; zero disables this limit.
max_iter (int | None) – Maximum iterations per output;
Noneuses the backend default. Condition or iteration limits are failures, not successful convergence.
optimization
Use minimize when evaluating a differentiable cost instead of observing
target responses. Supply an initial model, loss and typed method. Check
objective and stopping evidence in the optimization result. Sampling and
minibatch settings are explained in Objective minimization.
Objective minimization, separate from supervised least-squares regression.
- class torch_bsf.optimization.Iteration(iteration: int, loss: float, gradient_norm: float)[source]
Bases:
objectPre-update minibatch loss and gradient norm at a zero-based iteration.
- class torch_bsf.optimization.OptimizationResult(model: BezierModel, history: tuple[Iteration, ...], success: bool, status: str, message: str, objective: float, n_iterations: int, seed: int | None)[source]
Bases:
objectAn optimized copy, history and an honest stopping state.
Budget exhaustion and a user callback are not convergence certificates. Only all-fixed or a checked full-sample gradient tolerance yield success.
- require_success() OptimizationResult[source]
Raise if no convergence criterion was established.
- class torch_bsf.optimization.PreconditionedSGD(max_iter: int = 100, step_size: float = 0.05, batch_size: int | None = None, preconditioner: Literal['gram', 'none'] = 'gram', step_decay: Literal['constant', 'sqrt'] = 'constant', gradient_tolerance: float | None = None)[source]
Bases:
objectDomain-metric SGD with a fixed sample set and uniform random minibatches.
- torch_bsf.optimization.gram_matrix(space: BezierSpace, *, cell_weights: Mapping[int | Simploid, float] | None = None) Tensor[source]
Compute the exact Bernstein Gram matrix for the sampling distribution.
- torch_bsf.optimization.linear_scalarization(objective: Callable[[Tensor], Tensor]) Callable[[Tensor, Points], Tensor][source]
Turn differentiable objective values into a simplex-weighted loss.
- torch_bsf.optimization.minimize(model: BezierModel, loss: Callable[[Tensor, Points], Tensor], *, method: PreconditionedSGD = PreconditionedSGD(max_iter=100, step_size=0.05, batch_size=None, preconditioner='gram', step_decay='constant', gradient_tolerance=None), points: Points | None = None, n_samples: int = 100, seed: int | None = None, cell_weights: Mapping[int | Simploid, float] | None = None, penalty: QuadraticPenalty = QuadraticPenalty(ridge=0.0, smoothness=0.0), fixed: FixedControls | None = None, callback: Callable[[Iteration, BezierModel], bool | None] | None = None) OptimizationResult[source]
Minimize a differentiable loss on a copied model with domain-aware SGD.
- Parameters:
model – Initial model; never mutated.
loss –
loss(predicted_values, points)returning one loss per sample.method – Typed preconditioned SGD settings, independent of Lightning. Float16 and bfloat16 initial models are copied and promoted to float32 on the same device for basis evaluation, gradients and supported metric solves. The returned model then has float32 coefficients.
points – Fixed samples. Otherwise draw
n_sampleswith the stated cell weights.n_samples – Sampling count, local random seed and explicit cell distribution.
seed – Sampling count, local random seed and explicit cell distribution.
cell_weights – Sampling count, local random seed and explicit cell distribution.
penalty – Quadratic penalties and exact fixed coefficient values.
fixed – Quadratic penalties and exact fixed coefficient values.
callback – Receives a pre-update Iteration and the current model. Returning True stops without claiming convergence.
- Returns:
Copied model, pre-update history, final full-sample objective and stopping state.
- Return type:
ensemble
An ensemble combines explicitly selected compatible models for prediction. It is a deliberate modeling choice, not an automatic consequence of cross-validation. Compare its validation behavior with a single model refitted on all available training observations.
Explicit ensembling, independent of cross-validation and model selection.
io
save_model and load_model preserve compiled geometry and coefficients
in a versioned archive independent of filename extension. Control-point
export is a data export rather than a replacement for topology-aware
persistence. Store application parameter transforms alongside the archive.
Versioned model persistence for both single-cell and glued-cell spaces.
- torch_bsf.io.export_control_points(path: str | Path, model: BezierModel) None[source]
Export coefficient rows as CSV data, explicitly without pretending to save geometry.
- torch_bsf.io.load_model(path: str | Path) BezierModel[source]
Restore a validated model without importing a training framework.
Original mutable builder handles are not persisted. Loaded cells are addressed by the same stable integer IDs recorded in the model schema.
- torch_bsf.io.save_model(path: str | Path, model: BezierModel) None[source]
Save an explicit schema and non-pickled coefficient array atomically.
The format is a ZIP containing
model.jsonandcoefficients.npy, independent of the filename extension and Python class import paths.- Parameters:
path – Destination file; its parent directory must exist.
model – Any unified Bézier model, including a glued multi-cell space.
sampling
sample returns seeded parameter draws and grid returns deterministic
lattices. Neither obtains response measurements. Product factors retain
independent barycentric normalization, and assembled samples retain cell
IDs. See Sampling and grids and Grid sampling.
Domain-aware random and deterministic sampling, using local random generators.
- torch_bsf.sampling.grid(space: BezierSpace, resolution: int = 10, *, cells: Sequence[int | Simploid] | None = None) Points[source]
Create tensor-product barycentric grids on the selected cells.
resolution=0gives a cell’s barycenter. Boundary points of different cells remain distinct parameter-domain points even when their images agree.
- torch_bsf.sampling.sample(space: BezierSpace, n: int, *, method: Literal['random', 'sobol'] = 'random', seed: int | None = None, cell_weights: Mapping[int | Simploid, float] | None = None) Points[source]
Sample a space, uniformly within each selected cell’s simplex factors.
Cell probabilities default to equal weights on maximal cells, rather than an unspecified geometric volume. Sobol coordinates use exponential normalization of scrambled quasi-random uniforms. Cell selection remains categorical random sampling. Neither method resets global random state.
Examples
>>> from torch_bsf import BezierSpace >>> from torch_bsf.sampling import sample >>> sample(BezierSpace.simplex(2, degree=3), 5, seed=0).parameters.shape torch.Size([5, 3])
model_selection
Validation operations compare predictions on withheld observations and retain fold results. Degree selection changes capacity and requires an explicit final refit when using the core selection API. Learn output preprocessing inside each fold to prevent validation leakage.
Validation and degree search return explicit evidence rather than bare models.
- class torch_bsf.model_selection.CVResult(folds: tuple[FoldResult, ...], metrics: dict[str, float], success: bool)[source]
Bases:
objectValidation evidence; never an implicitly refitted model or ensemble.
- property models: tuple[BezierModel, ...][source]
Models available for explicitly constructing an Ensemble.
- class torch_bsf.model_selection.DegreeCandidate(degree: int | tuple[int, ...], space: BezierSpace, validation: CVResult)[source]
Bases:
objectA candidate degree and all its validation evidence.
- class torch_bsf.model_selection.DegreeSearchResult(candidates: tuple[DegreeCandidate, ...], best_index: int | None, searched_all: bool = True)[source]
Bases:
objectAll evaluated candidates and a selection, without an implicit full-data fit.
- class torch_bsf.model_selection.FoldResult(train_indices: Tensor, test_indices: Tensor, result: FitResult, metrics: dict[str, float], test_weight: float)[source]
Bases:
objectA fitted training fold with its untouched held-out indices and metrics.
- class torch_bsf.model_selection.KFold(n_splits: int = 5, shuffle: bool = True, seed: int | None = None)[source]
Bases:
objectLocal-generator k-fold splits; an impossible requested split count is an error.
- torch_bsf.model_selection.cross_validate(space: BezierSpace, samples: Samples, *, cv: KFold = KFold(n_splits=5, shuffle=True, seed=None), solver: DenseLeastSquares | SparseLeastSquares = DenseLeastSquares(rcond=None, diagnostics='basic'), penalty: QuadraticPenalty = QuadraticPenalty(ridge=0.0, smoothness=0.0), fixed: FixedControls | None = None) CVResult[source]
Fit each training fold and retain split, solver and held-out evidence.
Every fold must have positive observation weight in both its train and test portions. Failed solves remain in the result and make the whole CV fail; they are never silently dropped from model selection.
- torch_bsf.model_selection.select_degree(space: BezierSpace, samples: Samples, *, degrees: Sequence[int | Sequence[int]], cv: KFold = KFold(n_splits=5, shuffle=True, seed=None), solver: DenseLeastSquares | SparseLeastSquares = DenseLeastSquares(rcond=None, diagnostics='basic'), penalty: QuadraticPenalty = QuadraticPenalty(ridge=0.0, smoothness=0.0)) DegreeSearchResult[source]
Evaluate every specified degree on identical folds and record the result.
Invalid degrees raise; failed numerical solves remain visible and cannot win. To refit, explicitly call
fit(result.best_space, samples, ...).
active_learning
Active learning selects additional parameter locations from a candidate pool. The application still obtains the responses and appends observations. Assess progress on an external validation set rather than only the adaptively selected training rows. See Explicit ensembles and active learning.
Acquisition functions with explicit, mathematically relevant inputs.
- torch_bsf.active_learning.farthest_points(candidates: Points, observed: Points, n: int = 1) Points[source]
Greedily cover parameter coordinates, comparing distances within cells.
Unobserved cells are selected first. No dummy ensemble or duplicated geometry/dimension arguments are required for this density acquisition.
plotting
plot_model displays selected output coordinates and optional control
nets. Rendering density changes the evaluated mesh, not the fitted
coefficients. Higher-dimensional cells are shown through two-dimensional
faces; inspect interior residuals separately.
A single plotting entry point for single- and multiple-cell models.
- torch_bsf.plotting.plot_model(model: BezierModel, *, resolution: int = 20, coordinates: Sequence[int] | None = None, ax: Any = None, show_controls: bool = True, **kwargs: Any) Any[source]
Draw maximal cells and their control nets on shared Matplotlib axes.
Select two or three output coordinates for higher-dimensional models. Domains above dimension two are shown through their two-dimensional faces. Returns the axes. Matplotlib is imported only when plotting.
sklearn
BezierRegressor adapts the common fit to NumPy arrays and estimator
workflows. Features must encode valid barycentric blocks. Generic feature
standardization generally violates those constraints. See
Scikit-learn integration for degree searches and output shapes.
Scikit-learn integration using the same numerical problem and results.
- class torch_bsf.sklearn.BezierRegressor(degree: int | Sequence[int] = 3, *, dimensions: Sequence[int] | None = None, solver: DenseLeastSquares | SparseLeastSquares = DenseLeastSquares(rcond=None, diagnostics='basic'), penalty: QuadraticPenalty = QuadraticPenalty(ridge=0.0, smoothness=0.0), fixed: FixedControls | None = None)[source]
Bases:
RegressorMixin,BaseEstimatorFit a single simplex or product using explicit numerical settings.
- Parameters:
degree – Polynomial degree, or one degree per simplex factor.
dimensions – Factor dimensions; inferred as a single simplex from X when omitted.
solver – The same typed settings accepted by
torch_bsf.fit().penalty – The same typed settings accepted by
torch_bsf.fit().fixed – The same typed settings accepted by
torch_bsf.fit().
- result_, model_, diagnostics_
Complete fit evidence, fitted model, and numerical diagnostics.
- fit(X: Any, y: Any, sample_weight: Any = None) BezierRegressor[source]
Fit weighted observations and expose their complete FitResult.
- predict(X: Any) NDArray[Any][source]
Predict with the fitted model; preserve the target’s original rank.
- set_fit_request(*, sample_weight: bool | None | str = '$UNCHANGED$') BezierRegressor[source]
Configure whether metadata should be requested to be passed to the
fitmethod.Note that this method is only relevant when this estimator is used as a sub-estimator within a meta-estimator and metadata routing is enabled with
enable_metadata_routing=True(seesklearn.set_config()). Please check the User Guide on how the routing mechanism works.The options for each parameter are:
True: metadata is requested, and passed tofitif provided. The request is ignored if metadata is not provided.False: metadata is not requested and the meta-estimator will not pass it tofit.None: metadata is not requested, and the meta-estimator will raise an error if the user provides it.str: metadata should be passed to the meta-estimator with this given alias instead of the original name.
The default (
sklearn.utils.metadata_routing.UNCHANGED) retains the existing request. This allows you to change the request for some parameters and not others.Added in version 1.3.
- set_score_request(*, sample_weight: bool | None | str = '$UNCHANGED$') BezierRegressor[source]
Configure whether metadata should be requested to be passed to the
scoremethod.Note that this method is only relevant when this estimator is used as a sub-estimator within a meta-estimator and metadata routing is enabled with
enable_metadata_routing=True(seesklearn.set_config()). Please check the User Guide on how the routing mechanism works.The options for each parameter are:
True: metadata is requested, and passed toscoreif provided. The request is ignored if metadata is not provided.False: metadata is not requested and the meta-estimator will not pass it toscore.None: metadata is not requested, and the meta-estimator will raise an error if the user provides it.str: metadata should be passed to the meta-estimator with this given alias instead of the original name.
The default (
sklearn.utils.metadata_routing.UNCHANGED) retains the existing request. This allows you to change the request for some parameters and not others.Added in version 1.3.
splitting
Subdivision reparameterizes a model on smaller cells while preserving its represented function. Keep the child-to-parent coordinate maps when comparing values. Refitting child models is a separate operation that can change the function; see Exact local subdivision.
Exact local subdivision with explicit maps back to the parent domain.
- class torch_bsf.splitting.EdgeSplit(i: int, j: int, fraction: float = 0.5, factor: int = 0)[source]
Bases:
objectSplit one factor edge at a fraction strictly between zero and one.
- class torch_bsf.splitting.LongestEdge(fraction: float = 0.5)[source]
Bases:
objectSelect the longest chord of the selected cell’s control-net vertices.
- class torch_bsf.splitting.ParameterMap(parent_cell: int, dimensions: tuple[int, ...], split: EdgeSplit, part: Literal['A', 'B'])[source]
Bases:
objectAn affine child-to-parent map and its partial inverse.
- class torch_bsf.splitting.ResidualSplit(samples: Samples, resolution: int = 10)[source]
Bases:
objectMinimize the sum of unweighted child MSEs over a split-fraction grid.
Exact subdivision preserves the represented function. This policy balances residual grouping; it does not refit the children or guarantee error reduction.
- class torch_bsf.splitting.SubdivisionResult(models: tuple[BezierModel, BezierModel], maps: tuple[ParameterMap, ParameterMap])[source]
Bases:
objectTwo exact local models and their parent parameter maps.
This does not remesh neighboring cells of an assembled domain.
- torch_bsf.splitting.subdivide(model: BezierModel, *, cell: int = 0, policy: EdgeSplit | LongestEdge | ResidualSplit = LongestEdge(fraction=0.5)) SubdivisionResult[source]
Split a selected cell exactly, retaining its output dtype and device.
integrations.lightning
The optional Lightning adapter exposes the common model to a training framework. It does not select a different core regression algorithm. Use the core fit when solving the stated linear least-squares problem; see Objective minimization for framework-based objective optimization.
Opt-in Lightning execution for the common weighted regression objective.
- torch_bsf.integrations.lightning.regression_module(model: BezierModel, samples: Samples, *, learning_rate: float = 0.01, penalty: QuadraticPenalty = QuadraticPenalty(ridge=0.0, smoothness=0.0), fixed: FixedControls | None = None) Any[source]
Create a LightningModule from an explicit initial model and observations.
The caller owns Trainer settings and a one-item DataLoader (e.g.
[0]). Each step uses the complete normalized weighted objective, so batch and device partitioning cannot silently change regularization. This adapter supports one device; use core least squares for deterministic regression. Itsbezier_model()method returns an independent CPU float64 model.
integrations.mlflow
The optional MLflow integration records experiments without making the numerical core depend on a tracking service. Retain the parameter schema, units and reference-problem definition with recorded metrics so that artifacts can be interpreted outside their original run.
Explicit MLflow recording, independent of core numerical execution.
- torch_bsf.integrations.mlflow.log_result(result: FitResult, *, tracking_uri: str | None = None) str[source]
Record metrics, diagnostics and the versioned model artifact in a run.
Reuse an active run; otherwise start and finish one. Requires the mlflow extra. Returns the run ID. No autologging or pickled Python models are used. Unavailable placeholder models are never written; failure metrics and diagnostics are still recorded with
bsf.solution_available=False.
preprocessing
Explicit output transforms make scaling visible in training and inference. Fit statistics on training responses, transform exact fixed values in the same way, and invert predictions before reporting original-unit errors. See Output scaling for penalty and validation implications.
- class torch_bsf.preprocessing.MinMaxScaler[source]
Bases:
objectMin-max scaler that normalizes values to the [0, 1] range.
- scales[source]
Scale factors (max - min) per feature dimension. Dimensions where max equals min are set to 1 to avoid division by zero.
- Type:
- fit(values: Tensor) None[source]
Fit the scaler to the data.
Computes the per-feature minimum and scale (max - min) from
values.- Parameters:
values (torch.Tensor) – Input tensor of shape
(n_samples, n_features).
- fit_transform(values: Tensor) Tensor[source]
Fit the scaler to the data and return the normalized tensor.
- Parameters:
values (torch.Tensor) – Input tensor of shape
(n_samples, n_features).- Returns:
Normalized tensor of the same shape as
values, with each feature scaled to the [0, 1] range.- Return type:
- inverse_transform(values: Tensor) Tensor[source]
Reverse the normalization applied by
fit_transform().- Parameters:
values (torch.Tensor) – Normalized tensor of shape
(n_samples, n_features).- Returns:
Tensor rescaled back to the original value range.
- Return type:
- transform(values: Tensor) Tensor[source]
Normalize
valueswith the fitted minimum and scale.- Parameters:
values (torch.Tensor) – Tensor of shape
(n_samples, n_features).- Returns:
Transformed tensor of the same shape as
values.- Return type:
- class torch_bsf.preprocessing.NoneScaler[source]
Bases:
objectPass-through scaler that leaves values unchanged.
Useful as a no-op placeholder when normalization is not required, while still providing the same
fit(),fit_transform(), andinverse_transform()interface as the other scalers.- fit(values: Tensor) None[source]
No-op fit method included for API compatibility.
- Parameters:
values (torch.Tensor) – Input tensor (ignored).
- fit_transform(values: Tensor) Tensor[source]
Return
valuesunchanged.- Parameters:
values (torch.Tensor) – Input tensor of any shape.
- Returns:
The same tensor as
valueswithout any modification.- Return type:
- inverse_transform(values: Tensor) Tensor[source]
Return
valuesunchanged.- Parameters:
values (torch.Tensor) – Input tensor of any shape.
- Returns:
The same tensor as
valueswithout any modification.- Return type:
- transform(values: Tensor) Tensor[source]
Return
valuesunchanged.- Parameters:
values (torch.Tensor) – Input tensor of any shape.
- Returns:
The same tensor as
valueswithout any modification.- Return type:
- torch_bsf.preprocessing.NormalizeType[source]
"max"(MinMaxScaler),"std"(StdScaler),"quantile"(QuantileScaler) and"none"(NoneScaler).- Type:
The value normalization methods
alias of
Literal[‘max’, ‘std’, ‘quantile’, ‘none’]
- class torch_bsf.preprocessing.QuantileScaler[source]
Bases:
objectQuantile-based scaler that normalizes values using percentile-based ranges.
Values are scaled so that the
q-th percentile maps to 0 and the(1 - q)-th percentile maps to 1, without clipping values to this range. This makes the scaler robust to outliers compared toMinMaxScaler.- q[source]
The lower quantile fraction used as the effective minimum. Defaults to
0.05(5th percentile), ignoring the bottom and top 5% of values.- Type:
- scales[source]
Per-feature scale factors (
(1-q)-quantile minusq-quantile). Dimensions where the scale is zero are set to 1 to avoid division by zero.- Type:
- fit(values: Tensor) None[source]
Fit the scaler to the data.
Computes per-feature quantile bounds from
values.- Parameters:
values (torch.Tensor) – Input tensor of shape
(n_samples, n_features).
- fit_transform(values: Tensor) Tensor[source]
Fit the scaler to the data and return the scaled tensor.
- Parameters:
values (torch.Tensor) – Input tensor of shape
(n_samples, n_features).- Returns:
Scaled tensor of the same shape as
values. Values outside the fitted quantile range may exceed [0, 1].- Return type:
- inverse_transform(values: Tensor) Tensor[source]
Reverse the scaling applied by
fit_transform().- Parameters:
values (torch.Tensor) – Scaled tensor of shape
(n_samples, n_features).- Returns:
Tensor rescaled back to the original value range.
- Return type:
- transform(values: Tensor) Tensor[source]
Scale
valueswith the fitted quantile bounds.- Parameters:
values (torch.Tensor) – Tensor of shape
(n_samples, n_features).- Returns:
Transformed tensor of the same shape as
values.- Return type:
- class torch_bsf.preprocessing.Scaler(*args, **kwargs)[source]
Bases:
ProtocolProtocol for data scalers with a fit/transform interface.
All concrete scaler classes (
MinMaxScaler,StdScaler,QuantileScaler,NoneScaler) satisfy this protocol via structural subtyping — no explicit inheritance is required.- fit_transform(values: Tensor) Tensor[source]
Fit the scaler to
valuesand return the transformed tensor.
- inverse_transform(values: Tensor) Tensor[source]
Reverse the transformation applied by
fit_transform().
- class torch_bsf.preprocessing.StdScaler[source]
Bases:
objectStandard-score (z-score) scaler that normalizes values to zero mean and unit variance.
- stds[source]
Per-feature standard deviations computed during
fit(). Dimensions with zero standard deviation are set to 1 to avoid division by zero.- Type:
- fit(values: Tensor) None[source]
Fit the scaler to the data.
Computes the per-feature mean and standard deviation from
values.- Parameters:
values (torch.Tensor) – Input tensor of shape
(n_samples, n_features).
- fit_transform(values: Tensor) Tensor[source]
Fit the scaler to the data and return the standardized tensor.
- Parameters:
values (torch.Tensor) – Input tensor of shape
(n_samples, n_features).- Returns:
Standardized tensor of the same shape as
values, with each feature having zero mean and unit standard deviation.- Return type:
- inverse_transform(values: Tensor) Tensor[source]
Reverse the standardization applied by
fit_transform().- Parameters:
values (torch.Tensor) – Standardized tensor of shape
(n_samples, n_features).- Returns:
Tensor rescaled back to the original value range.
- Return type:
- transform(values: Tensor) Tensor[source]
Standardize
valueswith the fitted mean and standard deviation.- Parameters:
values (torch.Tensor) – Tensor of shape
(n_samples, n_features).- Returns:
Transformed tensor of the same shape as
values.- Return type:
- torch_bsf.preprocessing.make_scaler(normalize: Literal['max', 'std', 'quantile', 'none']) Scaler[source]
The scaler of a normalization method.
Every scaler is an affine map of each feature, so it commutes with Bézier simploids, whose Bernstein basis sums to one: transforming the control points transforms the values of the model.
- Parameters:
normalize –
"max","std","quantile"or"none".- Return type:
A new, unfitted scaler.
- Raises:
ValueError – If
normalizeis not one of the methods.
Examples
>>> import torch >>> from torch_bsf.preprocessing import make_scaler >>> scaler = make_scaler("max") >>> scaler.fit_transform(torch.tensor([[1.0], [3.0]])).tolist() [[0.0], [1.0]]