3333
3434def duffing_equation (y , t ):
3535 """Non-autonomous system"""
36+ # y1 is displacement and y2 is velocity
3637 y1 , y2 = y
38+ # u is sinusoidal input
3739 u = 2.5 * np .cos (2 * np .pi * t )
40+ # dydt is derivative of y1 and y2
3841 dydt = [y2 , - 0.1 * y2 + y1 - 0.25 * y1 ** 3 + u ]
3942 return dydt
4043
@@ -79,20 +82,26 @@ def auto_duffing_equation(y, t):
7982# In the phase portraits, it is shown that the system has two stable equilibria.
8083# We use one to generate training data and the other to generate test data.
8184
85+ # 10 s duration with 0.01 Hz sampling time,
86+ # so 1000 samples in total for each measurement
8287dur = 10
8388n_samples = 1000
89+ t = np .linspace (0 , dur , n_samples )
90+ # External excitation is the same for each measurement
91+ u = 2.5 * np .cos (2 * np .pi * t ).reshape (- 1 , 1 )
8492
93+ # Small additional white noise
8594rng = np .random .default_rng (12345 )
86- e_train = rng .normal (0 , 0.0004 , n_samples )
95+ e_train_0 = rng .normal (0 , 0.0004 , n_samples )
8796e_test = rng .normal (0 , 0.0004 , n_samples )
88- t = np .linspace (0 , dur , n_samples )
8997
98+ # Solve differential equation to get displacement as y
99+ # Initial condition at displacement 0.6 and velocity 0.8
90100sol = odeint (duffing_equation , [0.6 , 0.8 ], t )
91- u_train = 2.5 * np .cos (2 * np .pi * t ).reshape (- 1 , 1 )
92- y_train = sol [:, 0 ] + e_train
101+ y_train_0 = sol [:, 0 ] + e_train_0
93102
103+ # Initial condition at displacement 0.6 and velocity -0.8
94104sol = odeint (duffing_equation , [0.6 , - 0.8 ], t )
95- u_test = 2.5 * np .cos (2 * np .pi * t ).reshape (- 1 , 1 )
96105y_test = sol [:, 0 ] + e_test
97106
98107# %%
@@ -111,8 +120,8 @@ def auto_duffing_equation(y, t):
111120max_delay = 3
112121
113122narx_model = make_narx (
114- X = u_train ,
115- y = y_train ,
123+ X = u ,
124+ y = y_train_0 ,
116125 n_terms_to_select = 5 ,
117126 max_delay = max_delay ,
118127 poly_degree = 3 ,
@@ -129,17 +138,19 @@ def plot_prediction(ax, t, y_true, y_pred, title):
129138 ax .set_ylabel ("y(t)" )
130139
131140
132- narx_model .fit (u_train , y_train )
133- y_train_osa_pred = narx_model .predict (u_train , y_init = y_train [:max_delay ])
134- y_test_osa_pred = narx_model .predict (u_test , y_init = y_test [:max_delay ])
141+ # OSA NARX
142+ narx_model .fit (u , y_train_0 )
143+ y_train_0_osa_pred = narx_model .predict (u , y_init = y_train_0 [:max_delay ])
144+ y_test_osa_pred = narx_model .predict (u , y_init = y_test [:max_delay ])
135145
136- narx_model .fit (u_train , y_train , coef_init = "one_step_ahead" )
137- y_train_msa_pred = narx_model .predict (u_train , y_init = y_train [:max_delay ])
138- y_test_msa_pred = narx_model .predict (u_test , y_init = y_test [:max_delay ])
146+ # MSA NARX
147+ narx_model .fit (u , y_train_0 , coef_init = "one_step_ahead" )
148+ y_train_0_msa_pred = narx_model .predict (u , y_init = y_train_0 [:max_delay ])
149+ y_test_msa_pred = narx_model .predict (u , y_init = y_test [:max_delay ])
139150
140151fig , ax = plt .subplots (2 , 2 , figsize = (8 , 6 ))
141- plot_prediction (ax [0 , 0 ], t , y_train , y_train_osa_pred , "OSA NARX on Train" )
142- plot_prediction (ax [0 , 1 ], t , y_train , y_train_msa_pred , "MSA NARX on Train" )
152+ plot_prediction (ax [0 , 0 ], t , y_train_0 , y_train_0_osa_pred , "OSA NARX on Train 0 " )
153+ plot_prediction (ax [0 , 1 ], t , y_train_0 , y_train_0_msa_pred , "MSA NARX on Train 0 " )
143154plot_prediction (ax [1 , 0 ], t , y_test , y_test_osa_pred , "OSA NARX on Test" )
144155plot_prediction (ax [1 , 1 ], t , y_test , y_test_msa_pred , "MSA NARX on Test" )
145156fig .tight_layout ()
@@ -152,14 +163,21 @@ def plot_prediction(ax, t, y_true, y_pred, title):
152163#
153164# The plot above shows that the NARX model cannot capture the dynamics at
154165# the left equilibrium shown in the phase portraits. To improve the performance, let us
155- # combine the training and test data for model training to include the dynamics of both
156- # equilibria. Here, we need to insert (at least max_delay number of) `np.nan` to
157- # indicate the model that training data
158- # and test data are from different measurement sessions. The plot shows that the
166+ # append another measurement session to the training data to include the dynamics of
167+ # both equilibria. Here, we need to insert (at least max_delay number of) `np.nan` to
168+ # indicate the model that the original training data and the appended data
169+ # are from different measurement sessions. The plot shows that the
159170# prediction performance of the NARX on test data has been largely improved.
160171
161- u_all = np .r_ [u_train , [[np .nan ]] * max_delay , u_test ]
162- y_all = np .r_ [y_train , [np .nan ] * max_delay , y_test ]
172+ e_train_1 = rng .normal (0 , 0.0004 , n_samples )
173+
174+ # Solve differential equation to get displacement as y
175+ # Initial condition at displacement 0.5 and velocity -1
176+ sol = odeint (duffing_equation , [0.5 , - 1 ], t )
177+ y_train_1 = sol [:, 0 ] + e_train_1
178+
179+ u_all = np .r_ [u , [[np .nan ]] * max_delay , u ]
180+ y_all = np .r_ [y_train_0 , [np .nan ] * max_delay , y_train_1 ]
163181narx_model = make_narx (
164182 X = u_all ,
165183 y = y_all ,
@@ -170,17 +188,21 @@ def plot_prediction(ax, t, y_true, y_pred, title):
170188)
171189
172190narx_model .fit (u_all , y_all )
173- y_train_osa_pred = narx_model .predict (u_train , y_init = y_train [:max_delay ])
174- y_test_osa_pred = narx_model .predict (u_test , y_init = y_test [:max_delay ])
191+ y_train_0_osa_pred = narx_model .predict (u , y_init = y_train_0 [:max_delay ])
192+ y_train_1_osa_pred = narx_model .predict (u , y_init = y_train_1 [:max_delay ])
193+ y_test_osa_pred = narx_model .predict (u , y_init = y_test [:max_delay ])
175194
176195narx_model .fit (u_all , y_all , coef_init = "one_step_ahead" )
177- y_train_msa_pred = narx_model .predict (u_train , y_init = y_train [:max_delay ])
178- y_test_msa_pred = narx_model .predict (u_test , y_init = y_test [:max_delay ])
179-
180- fig , ax = plt .subplots (2 , 2 , figsize = (8 , 6 ))
181- plot_prediction (ax [0 , 0 ], t , y_train , y_train_osa_pred , "OSA NARX on Train" )
182- plot_prediction (ax [0 , 1 ], t , y_train , y_train_msa_pred , "MSA NARX on Train" )
183- plot_prediction (ax [1 , 0 ], t , y_test , y_test_osa_pred , "OSA NARX on Test" )
184- plot_prediction (ax [1 , 1 ], t , y_test , y_test_msa_pred , "MSA NARX on Test" )
196+ y_train_0_msa_pred = narx_model .predict (u , y_init = y_train_0 [:max_delay ])
197+ y_train_1_msa_pred = narx_model .predict (u , y_init = y_train_1 [:max_delay ])
198+ y_test_msa_pred = narx_model .predict (u , y_init = y_test [:max_delay ])
199+
200+ fig , ax = plt .subplots (3 , 2 , figsize = (8 , 9 ))
201+ plot_prediction (ax [0 , 0 ], t , y_train_0 , y_train_0_osa_pred , "OSA NARX on Train 0" )
202+ plot_prediction (ax [0 , 1 ], t , y_train_0 , y_train_0_msa_pred , "MSA NARX on Train 0" )
203+ plot_prediction (ax [1 , 0 ], t , y_train_1 , y_train_1_osa_pred , "OSA NARX on Train 1" )
204+ plot_prediction (ax [1 , 1 ], t , y_train_1 , y_train_1_msa_pred , "MSA NARX on Train 1" )
205+ plot_prediction (ax [2 , 0 ], t , y_test , y_test_osa_pred , "OSA NARX on Test" )
206+ plot_prediction (ax [2 , 1 ], t , y_test , y_test_msa_pred , "MSA NARX on Test" )
185207fig .tight_layout ()
186208plt .show ()
0 commit comments