|
| 1 | +""" |
| 2 | +======================= |
| 3 | +Mulit-output NARX model |
| 4 | +======================= |
| 5 | +
|
| 6 | +.. currentmodule:: fastcan |
| 7 | +
|
| 8 | +In this example, we illustrate how to build a multi-output polynomial |
| 9 | +NARX model for time series prediction. |
| 10 | +""" |
| 11 | + |
| 12 | +# Authors: The fastcan developers |
| 13 | +# SPDX-License-Identifier: MIT |
| 14 | + |
| 15 | +# %% |
| 16 | +# Prepare data |
| 17 | +# ------------ |
| 18 | +# |
| 19 | +# First, a simulated time series dataset is generated from the following nonlinear |
| 20 | +# system. |
| 21 | +# |
| 22 | +# .. math:: |
| 23 | +# y_0(k) &=\\ |
| 24 | +# &0.5y_0(k-1) + 0.8y_1(k-1) + 0.3u_0(k)^2 + 2u_0(k-1)u_0(k-3) + |
| 25 | +# 1.5u_0(k-2)u_1(k-3) + 1\\ |
| 26 | +# y_1(k) &=\\ |
| 27 | +# &0.6y_1(k-1) - 0.2y_0(k-1)y_1(k-2) + 0.3u_1(k)^2 + 1.5u_1(k-2)u_0(k-3) |
| 28 | +# + 0.5 |
| 29 | +# |
| 30 | +# |
| 31 | +# where :math:`k` is the time index, |
| 32 | +# :math:`u_0` and :math:`u_1` are input signals, |
| 33 | +# and :math:`y_0` and :math:`y_1` are output signals. |
| 34 | + |
| 35 | +import numpy as np |
| 36 | + |
| 37 | +rng = np.random.default_rng(12345) |
| 38 | +n_samples = 1000 |
| 39 | +max_delay = 3 |
| 40 | +e0 = rng.normal(0, 0.1, n_samples) |
| 41 | +e1 = rng.normal(0, 0.02, n_samples) |
| 42 | +u0 = rng.uniform(0, 1, n_samples + max_delay) |
| 43 | +u1 = rng.normal(0, 0.1, n_samples + max_delay) |
| 44 | +y0 = np.zeros(n_samples + max_delay) |
| 45 | +y1 = np.zeros(n_samples + max_delay) |
| 46 | +for i in range(max_delay, n_samples + max_delay): |
| 47 | + y0[i] = ( |
| 48 | + 0.5 * y0[i - 1] |
| 49 | + + 0.8 * y1[i - 1] |
| 50 | + + 0.3 * u0[i] ** 2 |
| 51 | + + 2 * u0[i - 1] * u0[i - 3] |
| 52 | + + 1.5 * u0[i - 2] * u1[i - 3] |
| 53 | + + 1 |
| 54 | + ) |
| 55 | + y1[i] = ( |
| 56 | + 0.6 * y1[i - 1] |
| 57 | + - 0.2 * y0[i - 1] * y1[i - 2] |
| 58 | + + 0.3 * u1[i] ** 2 |
| 59 | + + 1.5 * u1[i - 2] * u0[i - 3] |
| 60 | + + 0.5 |
| 61 | + ) |
| 62 | +y = np.c_[y0[max_delay:] + e0, y1[max_delay:] + e1] |
| 63 | +X = np.c_[u0[max_delay:], u1[max_delay:]] |
| 64 | + |
| 65 | + |
| 66 | +# %% |
| 67 | +# Identify the mulit-output NARX model |
| 68 | +# ------------------------------------ |
| 69 | +# We provide :meth:`narx.make_narx` to automatically find the model |
| 70 | +# structure. `n_terms_to_select` can be a list to indicate the number |
| 71 | +# of terms (excluding intercept) for each output. |
| 72 | + |
| 73 | +from fastcan.narx import make_narx, print_narx |
| 74 | + |
| 75 | +narx_model = make_narx( |
| 76 | + X=X, |
| 77 | + y=y, |
| 78 | + n_terms_to_select=[5, 4], |
| 79 | + max_delay=3, |
| 80 | + poly_degree=2, |
| 81 | + verbose=0, |
| 82 | +).fit(X, y) |
| 83 | + |
| 84 | +print_narx(narx_model, term_space=30) |
| 85 | + |
| 86 | + |
| 87 | +# %% |
| 88 | +# Plot NARX prediction performance |
| 89 | +# -------------------------------- |
| 90 | + |
| 91 | +import matplotlib.pyplot as plt |
| 92 | +from sklearn.metrics import r2_score |
| 93 | + |
| 94 | +y_pred = narx_model.predict( |
| 95 | + X[:100], |
| 96 | + y_init=y[: narx_model.max_delay_], # Set the initial values of the prediction to |
| 97 | + # the true values |
| 98 | +) |
| 99 | + |
| 100 | +plt.plot(y[:100], label="True") |
| 101 | +plt.plot(y_pred, label="Predicted") |
| 102 | +plt.xlabel("Time index k") |
| 103 | +plt.legend(["y0 True", "y1 True", "y0 Pred", "y1 Pred"], loc="right") |
| 104 | +plt.title(f"NARX prediction R-squared: {r2_score(y[:100], y_pred):.5f}") |
| 105 | +plt.show() |
0 commit comments