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: object

Cell-local basis supporting products, transpose products and explicit export.

matmat(coefficients: Tensor) → Tensor[source]

Evaluate a coefficient matrix, accumulating only local contributions.

rmatmat(values: Tensor) → Tensor[source]

Apply the transpose, summing repeated shared coefficient columns.

to_dense() → Tensor[source]

Explicitly materialize the full design matrix, for a dense solve.

class torch_bsf.core.BezierModel(space: BezierSpace, coefficients: Any)[source]

Bases: Module

A 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.

forward(points: Points | Tensor) → Tensor[source]

Evaluate points in their original row order, differentiable in both inputs and coefficients.

property n_outputs: int[source]

Dimension of each predicted value.

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: object

Immutable polynomial space, independent of learned values and output dimension.

Construct a simplex with simplex(), a product with simploid(), or compile a mutable topology builder with on_set().

Examples

>>> from torch_bsf import BezierSpace
>>> space = BezierSpace.simplex(dimension=2, degree=3)
>>> space.n_controls
10
adjacency() → Tensor[source]

Return a copy of the undirected control-net edges.

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.

property n_cells: int[source]

Number of addressable non-degenerate cells, including faces.

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.

validate(points: Points) → None[source]

Check IDs, finite coordinates and simplex membership of each factor.

property width: int[source]

Width used when packing mixed-cell coordinate batches.

with_degree(degree: int | Sequence[int]) → BezierSpace[source]

Recompile the same frozen domain at another degree for model selection.

class torch_bsf.core.Cell(dimensions: tuple[int, ...], degree: tuple[int, ...], controls: tuple[int, ...])[source]

Bases: object

A compiled cell’s factor dimensions, degrees and global coefficient rows.

property width: int[source]

Number of concatenated barycentric coordinates.

class torch_bsf.core.FixedControls(values: Mapping[int, Any])[source]

Bases: object

Explicit fixed values keyed by globally shared coefficient row.

classmethod from_cells(space: BezierSpace, values: Sequence[tuple[int | Simploid, Sequence[Any], Any]]) → FixedControls[source]

Resolve local references and reject contradictory shared values.

class torch_bsf.core.Points(parameters: Any, cell_ids: Any = None)[source]

Bases: object

Barycentric coordinate rows and their cell IDs, in prediction order.

classmethod from_cells(space: BezierSpace, data: Sequence[tuple[int | Simploid, Any]]) → Points[source]

Pack ordered cell-local coordinates without manual padding.

take(indices: Tensor) → Points[source]

Select rows while preserving their cell identities.

to(*, device: device | str = 'cpu', dtype: dtype = torch.float64) → Points[source]

Copy coordinates to an explicit compute dtype and device.

class torch_bsf.core.QuadraticPenalty(ridge: float = 0.0, smoothness: float = 0.0)[source]

Bases: object

Weights 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: object

Points with finite observed outputs and optional nonnegative weights.

classmethod from_cells(space: BezierSpace, data: Sequence[tuple[int | Simploid, Any, Any]], *, weights: Any = None) → Samples[source]

Pack ordered cell-local observations with different coordinate widths.

take(indices: Tensor) → Samples[source]

Select observations, keeping coordinates, outputs and weights aligned.

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: object

A 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 a DegeneracyMap, 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 < length if j >= 0, 0 <= i <= length if j == -1.

  • j – The vertex of factor i to repeat, or -1 to insert a degenerate edge as the new factor i.

Return type:

The degenerate simploid.

property degeneracy_map: tuple[tuple[int, tuple[int, ...]], ...][source]

The degeneracy map from base to this simploid (the identity map if not degenerate).

property dim: int[source]

The dimension \(\sum_i a_i\).

face(i: int, j: int) → Simploid[source]

The face \(d^{i+1}_j\): removes vertex j of factor i.

Parameters:
  • i – The factor index, 0 <= i < length.

  • j – The vertex index, 0 <= j <= a_i.

Return type:

The face.

faces() → Iterator[tuple[tuple[int, int], Simploid]][source]

Iterates ((i, j), face(i, j)) over all faces.

property is_degenerate: bool[source]

Whether this simploid is degenerate.

property is_vertex: bool[source]

Whether this is a vertex, i.e., its type is ().

property length: int[source]

The length \(n\) of the type.

property simploid_type: tuple[int, ...][source]

The type \((a_1, \ldots, a_n)\).

vertices() → list[Simploid][source]

The distinct vertices of this simploid.

class torch_bsf.geometry.SimploidalSet[source]

Bases: object

A 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:

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)
add_vertex() → Simploid[source]

Adds a new vertex.

Return type:

The vertex.

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 0 to dim (empty if the set is empty).

Raises:

ValueError – If field is 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 c holds the boundary of simploids(dim)[c] in the basis simploids(dim - 1).

  • boundary_matrix(0) has no rows.

Raises:

ValueError – If dim is 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 cone c of sigma satisfies c.face(k, 1) is sigma and c.face(k, 0) is the degeneracy of v with the type of sigma, where k = 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 simploid with face.degeneracy(i, j); the two must have the same type, and their boundaries are identified accordingly.

Parameters:
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
property dim: int[source]

The maximum dimension of the simploids (-1 if empty).

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 a and b are identified face by face (a.face(i, j) with b.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 how degenerate() 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 to sigma1 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)
simploids(dim: int | None = None) → list[Simploid][source]

The non-degenerate simploids, optionally only those of a given dimension.

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) plus ridge * ||P||² and smoothness * ||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 return success=False and emit ConvergenceWarning; require_success() raises with the result attached. If no finite iterate exists, diagnostics.solution_available is false and the model contains placeholder free coefficients rather than a fit.

Return type:

FitResult

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: object

A fitted surface and inferred barycentric rows in observation order.

objective_history contains 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: object

Assembled 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. fixed specifies 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 initial model 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.

tolerance bounds absolute change in root mean squared distance; parameter_tolerance bounds 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_values instead supplies every required face’s value-only samples, allowing arbitrary embeddings with initial. 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_cloud with all lower-face controls fixed exactly. Inspect faces for local stopping evidence; failure on a boundary stops assembly and returns the finite partial model with success=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: RuntimeError

Failure requested by require_success(), retaining the numerical result.

exception torch_bsf.results.ConvergenceWarning[source]

Bases: UserWarning

A 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: object

Compute provenance and observation/system diagnostics in original units.

driver identifies the executed backend (gelsd or lsmr), or is None if no solve was executed. output_success and output_statuses record termination for each output column, in prediction-column order. solution_available is false when a numerical failure left no finite iterate for at least one column; see FitResult for its placeholder. Residual norms and condition estimates from LSMR concern the augmented weighted, constrained system, not the unpenalized prediction MSE. Iterative fields use None for 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: object

A model, objective metrics, diagnostics and an explicit termination state.

Results are deliberately not callable. Predict with result.model(points). If diagnostics.solution_available is 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 ConvergenceError containing it.

warn_if_failed() → FitResult[source]

Expose a failed numerical solve without hiding its partial result.

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: object

Information about one explicitly identified least-squares matrix.

None means not computed, never zero. method distinguishes 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: object

CPU float64 SVD solve of the augmented problem, without normal equations.

Parameters:
  • rcond (float | None) – Relative singular-value cutoff. None uses machine epsilon times the larger matrix dimension. The actual absolute threshold is recorded.

  • diagnostics (Literal['basic', 'full']) – basic reuses the system SVD; full additionally 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: object

Optional 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; None uses 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: object

Pre-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: object

An 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: object

Domain-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_samples with 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:

OptimizationResult

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.

class torch_bsf.ensemble.Ensemble(models: Sequence[BezierModel])[source]

Bases: Module

Mean predictor over copied models with the same space and output dimension.

forward(points: Points | Tensor) → Tensor[source]

Return the ensemble mean, without changing the member models.

members(points: Points | Tensor) → Tensor[source]

Predict with every member, returning shape (n_models, n_points, n_outputs).

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.json and coefficients.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=0 gives 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: object

Validation 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: object

A 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: object

All 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: object

A 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: object

Local-generator k-fold splits; an impossible requested split count is an error.

split(n_samples: int) → tuple[tuple[Tensor, Tensor], ...][source]

Return disjoint train/test indices, covering every test sample once.

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.

torch_bsf.active_learning.query_by_committee(ensemble: Ensemble, candidates: Points, n: int = 1) → Points[source]

Select the largest predictive disagreement from supplied domain points.

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, BaseEstimator

Fit 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 fit method.

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 (see sklearn.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 to fit if provided. The request is ignored if metadata is not provided.

  • False: metadata is not requested and the meta-estimator will not pass it to fit.

  • 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.

Parameters:

sample_weight (str, True, False, or None, default=sklearn.utils.metadata_routing.UNCHANGED) – Metadata routing for sample_weight parameter in fit.

Returns:

self – The updated object.

Return type:

object

set_score_request(*, sample_weight: bool | None | str = '$UNCHANGED$') → BezierRegressor[source]

Configure whether metadata should be requested to be passed to the score method.

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 (see sklearn.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 to score if provided. The request is ignored if metadata is not provided.

  • False: metadata is not requested and the meta-estimator will not pass it to score.

  • 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.

Parameters:

sample_weight (str, True, False, or None, default=sklearn.utils.metadata_routing.UNCHANGED) – Metadata routing for sample_weight parameter in score.

Returns:

self – The updated object.

Return type:

object

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: object

Split one factor edge at a fraction strictly between zero and one.

class torch_bsf.splitting.LongestEdge(fraction: float = 0.5)[source]

Bases: object

Select 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: object

An affine child-to-parent map and its partial inverse.

from_parent(points: Points) → tuple[Points, Tensor][source]

Return child coordinates and a mask selecting parent rows in this piece.

to_parent(points: Points | Tensor) → Points[source]

Map child barycentric points to their parent cell.

class torch_bsf.splitting.ResidualSplit(samples: Samples, resolution: int = 10)[source]

Bases: object

Minimize 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: object

Two 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. Its bezier_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: object

Min-max scaler that normalizes values to the [0, 1] range.

mins[source]

Minimum values per feature dimension.

Type:

torch.Tensor

scales[source]

Scale factors (max - min) per feature dimension. Dimensions where max equals min are set to 1 to avoid division by zero.

Type:

torch.Tensor

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:

torch.Tensor

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:

torch.Tensor

transform(values: Tensor) → Tensor[source]

Normalize values with 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:

torch.Tensor

class torch_bsf.preprocessing.NoneScaler[source]

Bases: object

Pass-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(), and inverse_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 values unchanged.

Parameters:

values (torch.Tensor) – Input tensor of any shape.

Returns:

The same tensor as values without any modification.

Return type:

torch.Tensor

inverse_transform(values: Tensor) → Tensor[source]

Return values unchanged.

Parameters:

values (torch.Tensor) – Input tensor of any shape.

Returns:

The same tensor as values without any modification.

Return type:

torch.Tensor

transform(values: Tensor) → Tensor[source]

Return values unchanged.

Parameters:

values (torch.Tensor) – Input tensor of any shape.

Returns:

The same tensor as values without any modification.

Return type:

torch.Tensor

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: object

Quantile-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 to MinMaxScaler.

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:

float

mins[source]

Per-feature values at the q-th quantile, computed during fit().

Type:

torch.Tensor

scales[source]

Per-feature scale factors ((1-q)-quantile minus q-quantile). Dimensions where the scale is zero are set to 1 to avoid division by zero.

Type:

torch.Tensor

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:

torch.Tensor

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:

torch.Tensor

transform(values: Tensor) → Tensor[source]

Scale values with 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:

torch.Tensor

class torch_bsf.preprocessing.Scaler(*args, **kwargs)[source]

Bases: Protocol

Protocol 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(values: Tensor) → None[source]

Fit the scaler to values.

fit_transform(values: Tensor) → Tensor[source]

Fit the scaler to values and return the transformed tensor.

inverse_transform(values: Tensor) → Tensor[source]

Reverse the transformation applied by fit_transform().

transform(values: Tensor) → Tensor[source]

Transform values with the fitted statistics.

class torch_bsf.preprocessing.StdScaler[source]

Bases: object

Standard-score (z-score) scaler that normalizes values to zero mean and unit variance.

means[source]

Per-feature means computed during fit().

Type:

torch.Tensor

stds[source]

Per-feature standard deviations computed during fit(). Dimensions with zero standard deviation are set to 1 to avoid division by zero.

Type:

torch.Tensor

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:

torch.Tensor

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:

torch.Tensor

transform(values: Tensor) → Tensor[source]

Standardize values with 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.Tensor

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 normalize is 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]]