Scikit-learn Integration

PyTorch-BSF provides a high-level wrapper compatible with the Scikit-learn estimator API. This allows you to integrate Bézier simplex fitting into standard machine learning workflows, including pipelines and cross-validation tools.

The BezierSimploidRegressor

The BezierSimploidRegressor class inherits from sklearn.base.BaseEstimator and sklearn.base.RegressorMixin. It supports the standard fit(), predict(), and score() methods, and fits the model with torch_bsf.fit().

import numpy as np
from torch_bsf.sklearn import BezierSimploidRegressor

# Your data (parameters must be on a simplex)
X = np.array([[1.0, 0.0], [0.5, 0.5], [0.0, 1.0]])
y = np.array([[1.0], [0.75], [0.0]])

# Initialize and fit
reg = BezierSimploidRegressor(degree=2)
reg.fit(X, y)

# Predict
y_pred = reg.predict(X)

By default, each row of X is a point on a simplex with n_features vertices, and a Bézier simplex is fitted (a single feature gives a constant model). The fitted model is available as reg.model_.

Choosing a solver

The solver parameter selects how the control points are computed:

solver="lstsq" (default)

Since a Bézier simplex is linear in its control points, the squared error is minimized exactly by linear least squares on the Bernstein basis, solved in double precision. This is fast and accurate, especially for high-dimensional outputs (e.g., hundreds of values per sample), where training may stop far from the optimum. The optional ridge parameter adds the Tikhonov penalty \(\mathrm{ridge}\,\|P\|^2\) on the control points \(P\); without it, under-determined problems (fewer samples than control points) get the minimum-norm solution. The fitted model keeps the floating dtype of y (float64 for float64 or integer targets).

solver="trainer"

Trains the control points by gradient-based optimization with lightning.pytorch.Trainer for up to max_epochs epochs in single precision. Use it for minibatches and accelerators (batch_size, max_epochs, accelerator, devices, precision and trainer_kwargs). It requires the lightning extra (pip install "pytorch-bsf[lightning]"); without it, fit() raises ImportError. See Installation for all extras.

import numpy as np
from torch_bsf.sklearn import BezierSimploidRegressor

rng = np.random.default_rng(0)
raw = rng.random((41, 2))
X = raw / raw.sum(axis=1, keepdims=True)                  # 41 points on a segment
y = np.sin(np.outer(X[:, 0], np.linspace(1, 6, 256)))    # 256-dimensional values

reg = BezierSimploidRegressor(degree=10, ridge=1e-12)
reg.fit(X, y)
print(reg.predict(X).shape)
print(reg.score(X, y) > 0.999)
(41, 256)
True

init, freeze, smoothness_weight and normalize work with both solvers; the predictions are in the scale of y whatever normalize is. With solver="lstsq", the trainer-only options listed above are ignored, and a nonzero ridge with solver="trainer" raises ValueError.

All parameters work with get_params(), set_params() and clone(), so they can be searched by GridSearchCV like any other hyperparameter (e.g., {"ridge": [0.0, 1e-6, 1e-3]}).

Bézier simploids

To fit a Bézier simploid, i.e., a tensor product of Bézier simplices (see Simploidal Sets), pass its type as simploid_type. Each row of X is then the concatenation of the barycentric coordinates of every factor, and degree is the degree of every factor or one degree per factor. For example, on a prism (a triangle times a segment):

import numpy as np
from torch_bsf.simploidal import sample_simploid
from torch_bsf.sklearn import BezierSimploidRegressor

X = sample_simploid((2, 1), 40, seed=0).double().numpy()  # 3 + 2 columns
y = X[:, :1] ** 2 * X[:, 3:4]

reg = BezierSimploidRegressor(degree=(2, 1), simploid_type=(2, 1)).fit(X, y)
print(reg.simploid_type_, reg.model_.degree)
print(np.allclose(reg.predict(X), y, atol=1e-6))
(2, 1) (2, 1)
True

Integration with Pipelines

You can include BezierSimploidRegressor in a Scikit-learn Pipeline. This is particularly useful if you need to perform data preprocessing (though note that Bézier simplex fitting usually expects parameters to be on a simplex).

from sklearn.pipeline import Pipeline
from sklearn.preprocessing import StandardScaler

pipe = Pipeline([
    ('scaler', StandardScaler()),
    ('bsf', BezierSimploidRegressor(degree=3))
])

pipe.fit(X, y)

Using GridSearchCV

Since it’s a standard estimator, you can use GridSearchCV to find the optimal hyperparameters, such as the degree or smoothness weight.

from sklearn.model_selection import GridSearchCV

param_grid = {
    'degree': [2, 3, 4, 5],
    'smoothness_weight': [0.0, 0.01, 0.1]
}

grid = GridSearchCV(BezierSimploidRegressor(), param_grid, cv=5)
grid.fit(X, y)

print(f"Best parameters: {grid.best_params_}")

API Reference

See the API Documentation for a full list of supported parameters.