nemos.basis._basis.MultiplicativeBasis#
- class nemos.basis._basis.MultiplicativeBasis(basis1, basis2)[source]#
Bases:
CompositeBasisMixin
,Basis
Class representing the multiplication (external product) of two Basis objects.
- Parameters:
Examples
>>> # Generate sample data >>> import numpy as np >>> import nemos as nmo >>> X = np.random.normal(size=(30, 3))
>>> # define two basis and multiply >>> basis_1 = nmo.basis.BSplineEval(10) >>> basis_2 = nmo.basis.RaisedCosineLinearEval(15) >>> multiplicative_basis = basis_1 * basis_2 >>> multiplicative_basis MultiplicativeBasis( basis1=BSplineEval(n_basis_funcs=10, order=4), basis2=RaisedCosineLinearEval(n_basis_funcs=15, width=2.0), ) >>> # Can multiply or add another basis to the AdditiveBasis object >>> # This will cause the number of output features of the result basis to grow accordingly >>> basis_3 = nmo.basis.RaisedCosineLogEval(100) >>> multiplicative_basis_2 = multiplicative_basis * basis_3 >>> multiplicative_basis_2 MultiplicativeBasis( basis1=MultiplicativeBasis( basis1=BSplineEval(n_basis_funcs=10, order=4), basis2=RaisedCosineLinearEval(n_basis_funcs=15, width=2.0), ), basis2=RaisedCosineLogEval(n_basis_funcs=100, width=2.0, time_scaling=50.0, enforce_decay_to_zero=True), )
Attributes
Label for the basis.
Mode of operation, either
"conv"
or"eval"
.Compute the n-basis function runtime.
Number of features returned by the basis.
Methods
__init__
(basis1, basis2)compute_features
(*xi)Apply the basis transformation to the input data.
evaluate_on_grid
(*n_samples)Evaluate the basis set on a grid of equi-spaced sample points.
get_params
([deep])From scikit-learn, get parameters by inspecting init.
set_input_shape
(*xi)Set the expected input shape for the basis object.
set_params
(**params)Set the parameters of this estimator.
setup_basis
(*xi)Set all basis states.
split_by_feature
(x[, axis])Decompose an array along a specified axis into sub-arrays based on the number of expected inputs.
Turn the Basis into a TransformerBasis for use with scikit-learn.
- __add__(other)#
Add two Basis objects together.
- Parameters:
other (
Basis
) – The other Basis object to add.- Returns:
The resulting Basis object.
- Return type:
- __iter__()#
Makes basis iterable. Re-implemented for additive.
- __mul__(other)#
Multiply two Basis objects together.
- Parameters:
other (
Basis
) – The other Basis object to multiply.- Return type:
- Returns:
The resulting Basis object.
- __pow__(exponent)#
Exponentiation of a Basis object.
Define the power of a basis by repeatedly applying the method __multiply__. The exponent must be a positive integer.
- Parameters:
exponent (
int
) – Positive integer exponent- Return type:
- Returns:
The product of the basis with itself “exponent” times. Equivalent to
self * self * ... * self
.- Raises:
TypeError – If the provided exponent is not an integer.
ValueError – If the integer is zero or negative.
- __sklearn_clone__()#
Clone the basis while preserving attributes related to input shapes.
This method ensures that input shape attributes (e.g., _input_shape_product, _input_shape_) are preserved during cloning. Reinitializing the class as in the regular sklearn clone would drop these attributes, rendering cross-validation unusable. The method also handles recursive cloning for composite basis structures.
- Return type:
- compute_features(*xi)[source]#
Apply the basis transformation to the input data.
This method is designed to be a high-level interface for transforming input data using the basis functions defined by the subclass. Depending on the basis’ mode (‘Eval’ or ‘Conv’), it either evaluates the basis functions at the sample points or performs a convolution operation between the input data and the basis functions.
- Parameters:
*xi (ArrayLike | Tsd | TsdFrame | TsdTensor) – Input data arrays to be transformed. The shape and content requirements depend on the subclass and mode of operation (‘Eval’ or ‘Conv’).
- Return type:
FeatureMatrix
- Returns:
Transformed features. In ‘eval’ mode, it corresponds to the basis functions evaluated at the input samples. In ‘conv’ mode, it consists of convolved input samples with the basis functions. The output shape varies based on the subclass and mode.
Notes
Subclasses should implement how to handle the transformation specific to their basis function types and operation modes.
Examples
>>> import numpy as np >>> from nemos.basis import BSplineEval, RaisedCosineLogConv >>> from nemos.glm import GLM >>> basis1 = BSplineEval(n_basis_funcs=5, label="one_input") >>> basis2 = RaisedCosineLogConv(n_basis_funcs=6, window_size=10, label="two_inputs") >>> basis_mul = basis1 * basis2 >>> X_multi = basis_mul.compute_features(np.random.randn(20), np.random.randn(20, 2)) >>> print(X_multi.shape) # num_features: 60 = 5 * 2 * 6 (20, 60)
- evaluate_on_grid(*n_samples)[source]#
Evaluate the basis set on a grid of equi-spaced sample points.
The i-th axis of the grid will be sampled with
n_samples[i]
equi-spaced points. The method uses numpy.meshgrid withindexing="ij"
, returning matrix indexing instead of the default cartesian indexing, see Notes.- Parameters:
n_samples[0] – The number of points in the uniformly spaced grid. The length of n_samples must equal the number of combined bases.
... – The number of points in the uniformly spaced grid. The length of n_samples must equal the number of combined bases.
n_samples[n] – The number of points in the uniformly spaced grid. The length of n_samples must equal the number of combined bases.
n_samples (int)
- Return type:
Tuple
[Tuple
[NDArray], NDArray]- Returns:
*Xs – A tuple of arrays containing the meshgrid values, one element for each of the n dimension of the grid, where n equals to the number of inputs. The size of
Xs[i]
is(n_samples[0], ... , n_samples[n])
.Y – The basis function evaluated at the samples, shape
(n_samples[0], ... , n_samples[n], number of basis)
.
- Raises:
ValueError – If the time point number is inconsistent between inputs or if the number of inputs doesn’t match what the Basis object requires.
ValueError – If one of the n_samples is <= 0.
Notes
Setting
indexing = 'ij'
returns a meshgrid with matrix indexing. In the N-D case with inputs of size \(M_1,...,M_N\), outputs are of shape \((M_1, M_2, M_3, ....,M_N)\). This differs from the numpy.meshgrid default, which uses Cartesian indexing. For the same input, Cartesian indexing would return an output of shape \((M_2, M_1, M_3, ....,M_N)\).Examples
>>> import numpy as np >>> import nemos as nmo >>> mult_basis = nmo.basis.BSplineEval(4) * nmo.basis.RaisedCosineLinearEval(5) >>> X, Y, Z = mult_basis.evaluate_on_grid(10, 10)
- get_params(deep=True)#
From scikit-learn, get parameters by inspecting init.
- Parameters:
deep
- Return type:
dict
- Returns:
- out:
A dictionary containing the parameters. Key is the parameter name, value is the parameter value.
- property input_shape#
- property label: str#
Label for the basis.
- property mode#
Mode of operation, either
"conv"
or"eval"
.
- property n_basis_funcs#
Compute the n-basis function runtime.
This plays well with cross-validation where the number of basis function of the underlying bases can be changed. It must be read-only since the number of basis is determined by the two basis elements and the type of composition.
- property n_output_features#
Number of features returned by the basis.
Notes
The number of output features can be determined only when the number of inputs provided to the basis is known. Therefore, before the first call to
compute_features
, this property will returnNone
. After that call, or after setting the input shape withset_input_shape
,n_output_features
will be available.
- set_input_shape(*xi)[source]#
Set the expected input shape for the basis object.
This method sets the input shape for each component basis in the basis. One
xi
must be provided for each basis component, specified as an integer, a tuple of integers, or an array. The method calculates and stores the total number of output features based on the number of basis functions in each component and the provided input shapes.- Parameters:
*xi (int | tuple[int, …] | NDArray) –
The input shape specifications. For every k,``xi[k]`` can be: - An integer: Represents the dimensionality of the input. A value of
1
is treated as scalar input. - A tuple: Represents the exact input shape excluding the first axis (sample axis).All elements must be integers.
An array: The shape is extracted, excluding the first axis (assumed to be the sample axis).
- Raises:
ValueError – If a tuple is provided and it contains non-integer elements.
- Returns:
Returns the instance itself to allow method chaining.
- Return type:
self
Examples
>>> # Generate sample data >>> import numpy as np >>> import nemos as nmo
>>> # define an additive basis >>> basis_1 = nmo.basis.BSplineEval(5) >>> basis_2 = nmo.basis.RaisedCosineLinearEval(6) >>> basis_3 = nmo.basis.MSplineEval(7) >>> multiplicative_basis = basis_1 * basis_2 * basis_3
Specify the input shape using all 3 allowed ways: integer, tuple, array >>> _ = multiplicative_basis.set_input_shape(1, (2, 3), np.ones((10, 4, 5)))
Expected output features are: (5 * 6 * 7 bases) * (1 * 6 * 20 inputs) = 25200 >>> multiplicative_basis.n_output_features 25200
- set_params(**params)#
Set the parameters of this estimator.
The method works on simple estimators as well as on nested objects (such as
Pipeline
). The latter have parameters of the form<component>__<parameter>
so that it’s possible to update each component of a nested object.- Parameters:
**params (dict) – Estimator parameters.
- Returns:
self – Estimator instance.
- Return type:
estimator instance
- setup_basis(*xi)#
Set all basis states.
This method corresponds sklearn transformer
fit
. As fit, it must receive the input and it must set all basis states, i.e.kernel_
and all the states relative to the input shape. The difference between this method and the transformerfit
is in the expected input structure, where the transformerfit
method requires the inputs to be concatenated in a 2D array, while here each input is provided as a separate time series for each basis element.- Parameters:
xi (NDArray) – Input arrays.
- Return type:
- Returns:
The basis with ready for evaluation.
- split_by_feature(x, axis=1)[source]#
Decompose an array along a specified axis into sub-arrays based on the number of expected inputs.
This function takes an array (e.g., a design matrix or model coefficients) and splits it along a designated axis.
How it works:
If the basis expects an input shape
(n_samples, n_inputs)
, then the feature axis length will betotal_n_features = n_inputs * n_basis_funcs
. This axis is reshaped into dimensions(n_inputs, n_basis_funcs)
.If the basis expects an input of shape
(n_samples,)
, then the feature axis length will betotal_n_features = n_basis_funcs
. This axis is reshaped into(1, n_basis_funcs)
.
For example, if the input array
x
has shape(1, 2, total_n_features, 4, 5)
, then after applying this method, it will be reshaped into(1, 2, n_inputs, n_basis_funcs, 4, 5)
.The specified axis (
axis
) determines where the split occurs, and all other dimensions remain unchanged. See the example section below for the most common use cases.- Parameters:
x (NDArray) –
The input array to be split, representing concatenated features, coefficients, or other data. The shape of
x
along the specified axis must match the total number of features generated by the basis, i.e.,self.n_output_features
.Examples:
For a design matrix:
(n_samples, total_n_features)
For model coefficients:
(total_n_features,)
or(total_n_features, n_neurons)
.
axis (int, optional) – The axis along which to split the features. Defaults to 1. Use
axis=1
for design matrices (features along columns) andaxis=0
for coefficient arrays (features along rows). All other dimensions are preserved.
- Raises:
ValueError – If the shape of
x
along the specified axis does not matchself.n_output_features
.- Returns:
A dictionary where:
Key: Label of the basis.
Value: the array reshaped to:
(..., n_inputs, n_basis_funcs, ...)
- Return type:
dict
Examples
>>> import numpy as np >>> from nemos.basis import BSplineEval, RaisedCosineLogConv >>> from nemos.glm import GLM >>> basis1 = BSplineEval(n_basis_funcs=5, label="one_input") >>> basis2 = RaisedCosineLogConv(n_basis_funcs=6, window_size=10, label="two_inputs") >>> basis_mul = basis1 * basis2 >>> X_multi = basis_mul.compute_features(np.random.randn(20), np.random.randn(20, 2)) >>> print(X_multi.shape) # num_features: 60 = 5 * 2 * 6 (20, 60)
>>> # The multiplicative basis is a single 2D component. >>> split_features = basis_mul.split_by_feature(X_multi, axis=1) >>> for feature, arr in split_features.items(): ... print(f"{feature}: shape {arr.shape}") (one_input * two_inputs): shape (20, 2, 30)
- to_transformer()#
Turn the Basis into a TransformerBasis for use with scikit-learn.
- Return type:
Examples
Jointly cross-validating basis and GLM parameters with scikit-learn.
>>> import nemos as nmo >>> from sklearn.pipeline import Pipeline >>> from sklearn.model_selection import GridSearchCV >>> # load some data >>> X, y = np.random.normal(size=(30, 1)), np.random.poisson(size=30) >>> basis = nmo.basis.RaisedCosineLinearEval(10).set_input_shape(1).to_transformer() >>> glm = nmo.glm.GLM(regularizer="Ridge", regularizer_strength=1.) >>> pipeline = Pipeline([("basis", basis), ("glm", glm)]) >>> param_grid = dict( ... glm__regularizer_strength=(0.1, 0.01, 0.001, 1e-6), ... basis__n_basis_funcs=(3, 5, 10, 20, 100), ... ) >>> gridsearch = GridSearchCV( ... pipeline, ... param_grid=param_grid, ... cv=5, ... ) >>> gridsearch = gridsearch.fit(X, y)