Skip to content

Commit

Permalink
Merge pull request #140 from wilsonrljr/v0.4.2
Browse files Browse the repository at this point in the history
V0.4.2
  • Loading branch information
wilsonrljr authored Sep 3, 2024
2 parents 8b03f31 + 4eb4377 commit f878e83
Show file tree
Hide file tree
Showing 2 changed files with 54 additions and 32 deletions.
75 changes: 50 additions & 25 deletions sysidentpy/basis_function/_basis_function.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
"""Basis Function for NARMAX models."""

import warnings

from itertools import combinations_with_replacement
from typing import Optional
import numpy as np
Expand Down Expand Up @@ -79,15 +81,19 @@ def fit(
"""
# Create combinations of all columns based on its index
iterable_list = self.get_iterable_list(ylag, xlag, model_type)
combinations = list(combinations_with_replacement(iterable_list, self.degree))
iterable_list = self.get_iterable_list(ylag, xlag, model_type)
combination_list = list(
combinations_with_replacement(iterable_list, self.degree)
)
if predefined_regressors is not None:
combinations = [combinations[index] for index in predefined_regressors]
combination_list = [
combination_list[index] for index in predefined_regressors
]

psi = np.column_stack(
[
np.prod(data[:, combinations[i]], axis=1)
for i in range(len(combinations))
np.prod(data[:, combination_list[i]], axis=1)
for i in range(len(combination_list))
]
)
psi = psi[max_lag:, :]
Expand All @@ -97,8 +103,8 @@ def transform(
self,
data: np.ndarray,
max_lag: int = 1,
ylag: int=1,
xlag: int=1,
ylag: int = 1,
xlag: int = 1,
model_type: str = "NARMAX",
predefined_regressors: Optional[np.ndarray] = None,
):
Expand Down Expand Up @@ -202,7 +208,9 @@ def fit(
"""
# remove intercept (because the data always have the intercept)
if self.degree > 1:
data = Polynomial().fit(data, max_lag, ylag, xlag, model_type, predefined_regressors=None)
data = Polynomial().fit(
data, max_lag, ylag, xlag, model_type, predefined_regressors=None
)
data = data[:, 1:]
else:
data = data[max_lag:, 1:]
Expand Down Expand Up @@ -337,7 +345,9 @@ def fit(
):
# remove intercept (because the data always have the intercept)
if self.degree > 1:
data = Polynomial().fit(data, max_lag, ylag, xlag, model_type, predefined_regressors=None)
data = Polynomial().fit(
data, max_lag, ylag, xlag, model_type, predefined_regressors=None
)
data = data[:, 1:]
else:
data = data[max_lag:, 1:]
Expand Down Expand Up @@ -381,6 +391,8 @@ def transform(
The range of lags according to user definition.
xlag : ndarray of int
The range of lags according to user definition.
model_type : str
The type of the model (NARMAX, NAR or NFIR).
predefined_regressors: ndarray
Regressors to be filtered in the transformation.
Expand All @@ -392,6 +404,7 @@ def transform(
"""
return self.fit(data, max_lag, ylag, xlag, model_type, predefined_regressors)


class Bilinear(BaseBasisFunction):
r"""Build Bilinear basis function.
Expand All @@ -403,11 +416,11 @@ class Bilinear(BaseBasisFunction):
This is a special case of the Polynomial NARMAX model.
Bilinear system theory has been widely studied and it plays an important role in the context of continuous-time
systems. This is because, roughly speaking, the set of bilinear
systems is dense in the space of continuous-time systems and any continuous causal
functional can be arbitrarily well approximated by bilinear systems within any
bounded time interval (see for example Fliess and Normand-Cyrot 1982). Moreover,
Bilinear system theory has been widely studied and it plays an important role in the
context of continuous-time systems. This is because, roughly speaking, the set of
bilinear systems is dense in the space of continuous-time systems and any continuous
causal functional can be arbitrarily well approximated by bilinear systems within
any bounded time interval (see for example Fliess and Normand-Cyrot 1982). Moreover,
many real continuous-time processes are naturally in bilinear form. A few examples
are distillation columns (España and Landau 1978), nuclear and thermal control
processes (Mohler 1973).
Expand All @@ -427,7 +440,6 @@ class Bilinear(BaseBasisFunction):
degree increases. High degrees can cause overfitting.
"""


def __init__(
self,
degree: int = 2,
Expand Down Expand Up @@ -472,26 +484,39 @@ def fit(
"""
# Create combinations of all columns based on its index
iterable_list = self.get_iterable_list(ylag, xlag, model_type)
combinations = list(combinations_with_replacement(iterable_list, self.degree))
iterable_list = self.get_iterable_list(ylag, xlag, model_type)
combination_list = list(
combinations_with_replacement(iterable_list, self.degree)
)
if self.degree == 1:
Warning('You choose a bilinear basis function and nonlinear degree = 1. In this case, you have a linear polynomial model.')
warnings.warn(
"You choose a bilinear basis function and nonlinear degree = 1."
"In this case, you have a linear polynomial model.",
stacklevel=2,
)
else:
ny = self.get_max_ylag(ylag)
nx = self.get_max_xlag(xlag)
combination_ylag = list(combinations_with_replacement(list(range(1, ny + 1)), self.degree))
combination_xlag = list(combinations_with_replacement(list(range(ny + 1, nx + ny + 1)), self.degree))
combination_ylag = list(
combinations_with_replacement(list(range(1, ny + 1)), self.degree)
)
combination_xlag = list(
combinations_with_replacement(
list(range(ny + 1, nx + ny + 1)), self.degree
)
)
combinations_xy = combination_xlag + combination_ylag
combinations = list(set(combinations)-set(combinations_xy))
combination_list = list(set(combination_list) - set(combinations_xy))

if predefined_regressors is not None:
combinations = [combinations[index] for index in predefined_regressors]

combination_list = [
combination_list[index] for index in predefined_regressors
]

psi = np.column_stack(
[
np.prod(data[:, combinations[i]], axis=1)
for i in range(len(combinations))
np.prod(data[:, combination_list[i]], axis=1)
for i in range(len(combination_list))
]
)
psi = psi[max_lag:, :]
Expand Down
11 changes: 4 additions & 7 deletions sysidentpy/basis_function/basis_function_base.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
"""Base class for Basis Function."""

from itertools import combinations_with_replacement, chain
from itertools import chain
from abc import ABCMeta, abstractmethod
from typing import Optional

Expand All @@ -21,6 +21,7 @@ def fit(
max_lag: int = 1,
ylag: int = 1,
xlag: int = 1,
model_type: str = "NARMAX",
predefined_regressors: Optional[np.ndarray] = None,
):
"""Abstract method."""
Expand All @@ -32,6 +33,7 @@ def transform(
max_lag: int = 1,
ylag: int = 1,
xlag: int = 1,
model_type: str = "NARMAX",
predefined_regressors: Optional[np.ndarray] = None,
) -> np.ndarray:
"""Abstract methods."""
Expand Down Expand Up @@ -71,10 +73,7 @@ def get_max_xlag(self, xlag: int = 1):
return nx

def get_iterable_list(
self,
ylag: int = 1,
xlag: int = 1,
model_type: str = "NARMAX"
self, ylag: int = 1, xlag: int = 1, model_type: str = "NARMAX"
):
"""Get iterable list.
Expand All @@ -97,12 +96,10 @@ def get_iterable_list(
ny = self.get_max_ylag(ylag)
nx = self.get_max_xlag(xlag)
iterable_list = list(range(ny + nx + 1))
combinations = list(combinations_with_replacement(iterable_list, self.degree))
elif model_type == "NAR":
ny = self.get_max_ylag(ylag)
iterable_list = list(range(ny + 1))
else:
nx = self.get_max_xlag(xlag)
iterable_list = list(range(nx + 1))
return iterable_list

0 comments on commit f878e83

Please sign in to comment.