Skip to content

Commit

Permalink
Merge branch 'master' into simpler-inputs
Browse files Browse the repository at this point in the history
  • Loading branch information
Jacob-Stevens-Haas committed Jul 6, 2023
2 parents bce0ea6 + b8440e8 commit 20fa916
Show file tree
Hide file tree
Showing 16 changed files with 74 additions and 613 deletions.
3 changes: 2 additions & 1 deletion docs/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,8 @@
html_sourcelink_suffix = ""

intersphinx_mapping = {
"derivative": ("https://derivative.readthedocs.io/en/latest/", None)
"derivative": ("https://derivative.readthedocs.io/en/latest/", None),
"sklearn": ("https://scikit-learn.org/stable/", None),
}

# -- Extensions to the Napoleon GoogleDocstring class ---------------------
Expand Down
102 changes: 15 additions & 87 deletions pysindy/feature_library/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,29 +25,8 @@ class BaseFeatureLibrary(TransformerMixin):
Forces subclasses to implement ``fit``, ``transform``,
and ``get_feature_names`` methods.
Parameters
----------
library_ensemble : boolean, optional (default False)
Whether or not to use library bagging (regress on subset of the
candidate terms in the library)
ensemble_indices : integer array, optional (default [0])
The indices to use for ensembling the library.
"""

def __init__(self, library_ensemble=None, ensemble_indices=[0]):
if library_ensemble is not None:
warnings.warn(
"Library ensembling is no longer performed by feature libraries. Use "
"EnsemblingOptimizer to fit an ensemble model.",
DeprecationWarning,
)
self.library_ensemble = library_ensemble
if np.any(np.asarray(ensemble_indices) < 0):
raise ValueError("Library ensemble indices must be 0 or positive integers.")
self.ensemble_indices = ensemble_indices

def validate_input(self, x, *args, **kwargs):
return validate_no_reshape(x, *args, **kwargs)

Expand Down Expand Up @@ -145,29 +124,6 @@ def get_feature_names(self, input_features=None):
"""
raise NotImplementedError

def _ensemble(self, xp):
"""
If library bagging, return xp without
the terms at ensemble_indices
"""
warnings.warn(
"Library ensembling is no longer performed by feature libraries. Use "
"EnsemblingOptimizer to fit an ensemble model.",
UserWarning,
)

if self.library_ensemble:
if self.n_output_features_ <= len(self.ensemble_indices):
raise ValueError(
"Error: you are trying to chop more library terms "
"than are available to remove!"
)
inds = range(self.n_output_features_)
inds = np.delete(inds, self.ensemble_indices)
return [x[..., inds] for x in xp]
else:
return xp

def __add__(self, other):
return ConcatLibrary([self, other])

Expand Down Expand Up @@ -222,14 +178,6 @@ class ConcatLibrary(BaseFeatureLibrary):
libraries : list of libraries
Library instances to be applied to the input matrix.
library_ensemble : boolean, optional (default False)
Whether or not to use library bagging (regress on subset of the
candidate terms in the library).
ensemble_indices : integer array, optional (default [0])
The indices to use for ensembling the library. For instance, if
ensemble_indices = [0], it chops off the first column of the library.
Attributes
----------
n_features_in_ : int
Expand Down Expand Up @@ -257,12 +205,8 @@ class ConcatLibrary(BaseFeatureLibrary):
def __init__(
self,
libraries: list,
library_ensemble=False,
ensemble_indices=[0],
):
super(ConcatLibrary, self).__init__(
library_ensemble=library_ensemble, ensemble_indices=ensemble_indices
)
super().__init__()
self.libraries = libraries

@x_sequence_or_item
Expand Down Expand Up @@ -319,8 +263,6 @@ def transform(self, x_full):

xp = AxesArray(xp, comprehend_axes(xp))
xp_full.append(xp)
if self.library_ensemble:
xp_full = self._ensemble(xp_full)
return xp_full

def get_feature_names(self, input_features=None):
Expand Down Expand Up @@ -355,20 +297,12 @@ class TensoredLibrary(BaseFeatureLibrary):
libraries : list of libraries
Library instances to be applied to the input matrix.
library_ensemble : boolean, optional (default False)
Whether or not to use library bagging (regress on subset of the
candidate terms in the library).
inputs_per_library_ : Sequence of Sequences of ints (default None)
list that specifies which input indexes should be passed as
inputs for each of the individual feature libraries.
length must equal the number of feature libraries. Default is
that all inputs are used for every library.
ensemble_indices : integer array, optional (default [0])
The indices to use for ensembling the library. For instance, if
ensemble_indices = [0], it chops off the first column of the library.
Attributes
----------
libraries_ : list of libraries
Expand Down Expand Up @@ -399,14 +333,10 @@ class TensoredLibrary(BaseFeatureLibrary):
def __init__(
self,
libraries: list,
library_ensemble=False,
inputs_per_library: Optional[Sequence[Sequence[int]]] = None,
ensemble_indices=[0],
):
super(TensoredLibrary, self).__init__(
library_ensemble=library_ensemble, ensemble_indices=ensemble_indices
)
self.libraries_ = libraries
super().__init__()
self.libraries = libraries
self.inputs_per_library = inputs_per_library

def _combinations(self, lib_i, lib_j):
Expand Down Expand Up @@ -470,13 +400,13 @@ def fit(self, x_full, y=None):
# If parameter is not set, use all the inputs
if self.inputs_per_library is None:
self.inputs_per_library = list(
repeat(list(range(n_features)), len(self.libraries_))
repeat(list(range(n_features)), len(self.libraries))
)

# First fit all libs provided below
fitted_libs = [
lib.fit([x[..., _unique(self.inputs_per_library[i])] for x in x_full], y)
for i, lib in enumerate(self.libraries_)
for i, lib in enumerate(self.libraries)
]

# Calculate the sum of output features
Expand All @@ -486,7 +416,7 @@ def fit(self, x_full, y=None):
self.n_output_features_ *= osize

# Save fitted libs
self.libraries_ = fitted_libs
self.libraries = fitted_libs

return self

Expand All @@ -511,17 +441,17 @@ def transform(self, x_full):
xp_full = []
for x in x_full:
xp = []
for i in range(len(self.libraries_)):
lib_i = self.libraries_[i]
for i in range(len(self.libraries)):
lib_i = self.libraries[i]
if self.inputs_per_library is None:
xp_i = lib_i.transform([x])[0]
else:
xp_i = lib_i.transform(
[x[..., _unique(self.inputs_per_library[i])]]
)[0]

for j in range(i + 1, len(self.libraries_)):
lib_j = self.libraries_[j]
for j in range(i + 1, len(self.libraries)):
lib_j = self.libraries[j]
xp_j = lib_j.transform(
[x[..., _unique(self.inputs_per_library[j])]]
)[0]
Expand All @@ -531,8 +461,6 @@ def transform(self, x_full):
xp = np.concatenate(xp, axis=xp[0].ax_coord)
xp = AxesArray(xp, comprehend_axes(xp))
xp_full.append(xp)
if self.library_ensemble:
xp_full = self._ensemble(xp_full)
return xp_full

def get_feature_names(self, input_features=None):
Expand All @@ -549,8 +477,8 @@ def get_feature_names(self, input_features=None):
output_feature_names : list of string, length n_output_features
"""
feature_names = list()
for i in range(len(self.libraries_)):
lib_i = self.libraries_[i]
for i in range(len(self.libraries)):
lib_i = self.libraries[i]
if input_features is None:
input_features_i = [
"x%d" % k for k in _unique(self.inputs_per_library[i])
Expand All @@ -560,8 +488,8 @@ def get_feature_names(self, input_features=None):
_unique(self.inputs_per_library[i])
].tolist()
lib_i_feat_names = lib_i.get_feature_names(input_features_i)
for j in range(i + 1, len(self.libraries_)):
lib_j = self.libraries_[j]
for j in range(i + 1, len(self.libraries)):
lib_j = self.libraries[j]
if input_features is None:
input_features_j = [
"x%d" % k for k in _unique(self.inputs_per_library[j])
Expand All @@ -577,7 +505,7 @@ def get_feature_names(self, input_features=None):
return feature_names

def calc_trajectory(self, diff_method, x, t):
return self.libraries_[0].calc_trajectory(diff_method, x, t)
return self.libraries[0].calc_trajectory(diff_method, x, t)


def _unique(s: Sequence) -> Sequence:
Expand Down
15 changes: 1 addition & 14 deletions pysindy/feature_library/custom_library.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,13 +41,6 @@ class CustomLibrary(BaseFeatureLibrary):
will be included.
If False, all combinations will be included.
library_ensemble : boolean, optional (default False)
Whether or not to use library bagging (regress on subset of the
candidate terms in the library)
ensemble_indices : integer array, optional (default [0])
The indices to use for ensembling the library.
include_bias : boolean, optional (default False)
If True (default), then include a bias column, the feature in which
all polynomial powers are zero (i.e. a column of ones - acts as an
Expand Down Expand Up @@ -92,13 +85,9 @@ def __init__(
library_functions,
function_names=None,
interaction_only=True,
library_ensemble=False,
ensemble_indices=[0],
include_bias=False,
):
super(CustomLibrary, self).__init__(
library_ensemble=library_ensemble, ensemble_indices=ensemble_indices
)
super().__init__()
self.functions = library_functions
self.function_names = function_names
if function_names and (
Expand Down Expand Up @@ -219,6 +208,4 @@ def transform(self, x_full):

xp = AxesArray(xp, comprehend_axes(xp))
xp_full.append(xp)
if self.library_ensemble:
xp_full = self._ensemble(xp_full)
return xp_full
15 changes: 1 addition & 14 deletions pysindy/feature_library/fourier_library.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,13 +25,6 @@ class FourierLibrary(BaseFeatureLibrary):
include_cos : boolean, optional (default True)
If True, include cosine terms in the library.
library_ensemble : boolean, optional (default False)
Whether or not to use library bagging (regress on subset of the
candidate terms in the library)
ensemble_indices : integer array, optional (default 0)
The indices to use for ensembling the library.
Attributes
----------
n_features_in_ : int
Expand Down Expand Up @@ -61,12 +54,8 @@ def __init__(
n_frequencies=1,
include_sin=True,
include_cos=True,
library_ensemble=False,
ensemble_indices=[0],
):
super(FourierLibrary, self).__init__(
library_ensemble=library_ensemble, ensemble_indices=ensemble_indices
)
super().__init__()
if not (include_sin or include_cos):
raise ValueError("include_sin and include_cos cannot both be False")
if n_frequencies < 1 or not isinstance(n_frequencies, int):
Expand Down Expand Up @@ -164,6 +153,4 @@ def transform(self, x_full):
idx += 1
xp = AxesArray(xp, comprehend_axes(xp))
xp_full.append(xp)
if self.library_ensemble:
xp_full = self._ensemble(xp_full)
return xp_full
16 changes: 1 addition & 15 deletions pysindy/feature_library/generalized_library.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,14 +42,6 @@ class GeneralizedLibrary(BaseFeatureLibrary):
length must equal the number of feature libraries. Default is
that all inputs are used for every library.
library_ensemble : boolean, optional (default False)
Whether or not to use library bagging (regress on subset of the
candidate terms in the library).
ensemble_indices : integer array, optional (default [0])
The indices to use for ensembling the library. For instance, if
ensemble_indices = [0], it chops off the first column of the library.
Attributes
----------
self.libraries_full_: list[BaseFeatureLibrary]
Expand Down Expand Up @@ -82,13 +74,9 @@ def __init__(
libraries: list,
tensor_array=None,
inputs_per_library: Optional[Sequence[Sequence[int]]] = None,
library_ensemble=False,
ensemble_indices=[0],
exclude_libraries=[],
):
super(GeneralizedLibrary, self).__init__(
library_ensemble=library_ensemble, ensemble_indices=ensemble_indices
)
super().__init__()
if len(libraries) > 0:
self.libraries = libraries

Expand Down Expand Up @@ -252,8 +240,6 @@ def transform(self, x_full):

xp = AxesArray(np.concatenate(xps, axis=xps[0].ax_coord), xps[0].__dict__)
xp_full = xp_full + [xp]
if self.library_ensemble:
xp_full = self._ensemble(xp_full)
return xp_full

def get_feature_names(self, input_features=None):
Expand Down
18 changes: 0 additions & 18 deletions pysindy/feature_library/identity_library.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,6 @@ class IdentityLibrary(BaseFeatureLibrary):
The total number of output features. The number of output features
is equal to the number of input features.
library_ensemble : boolean, optional (default False)
Whether or not to use library bagging (regress on subset of the
candidate terms in the library)
ensemble_indices : integer array, optional (default [0])
The indices to use for ensembling the library.
Examples
--------
>>> import numpy as np
Expand All @@ -39,15 +32,6 @@ class IdentityLibrary(BaseFeatureLibrary):
['x0', 'x1']
"""

def __init__(
self,
library_ensemble=False,
ensemble_indices=[0],
):
super(IdentityLibrary, self).__init__(
library_ensemble=library_ensemble, ensemble_indices=ensemble_indices
)

def get_feature_names(self, input_features=None):
"""
Return feature names for output features
Expand Down Expand Up @@ -114,6 +98,4 @@ def transform(self, x_full):
raise ValueError("x shape does not match training shape")

xp_full = xp_full + [x.copy()]
if self.library_ensemble:
xp_full = self._ensemble(xp_full)
return xp_full
4 changes: 0 additions & 4 deletions pysindy/feature_library/parameterized_library.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,8 +52,6 @@ def __init__(
feature_library: BaseFeatureLibrary = PolynomialLibrary(),
num_parameters: int = 3,
num_features: int = 3,
library_ensemble=False,
ensemble_indices=[0],
):
if num_parameters <= 0 or num_features <= 0:
raise ValueError("Both num_parameter and num_feature must be positive.")
Expand All @@ -67,8 +65,6 @@ def __init__(
tensor_array=[[1, 1]],
exclude_libraries=[0, 1],
inputs_per_library=inputs_per_library,
library_ensemble=library_ensemble,
ensemble_indices=ensemble_indices,
)

def calc_trajectory(self, diff_method, x, t):
Expand Down
Loading

0 comments on commit 20fa916

Please sign in to comment.