def _predict_update_no_noise(self, t): # extract t-1 parameters (時刻t-1のパラメータ取り出す) F = _last_dims(self.F, t - 1, 2) Q = _last_dims(self.Q, t - 1, 2) b = _last_dims(self.b, t - 1, 1) # predict t distribution (時刻tの予測分布の計算) self.x_pred[t] = F @ self.x_filt[t - 1] + b self.V_pred[t] = F @ self.V_filt[t - 1] @ F.T + Q
def smooth(self): ''' T : length of data y (時系列の長さ) x_smooth [n_time, n_dim_sys] {numpy-array, float} : mean of hidden state distributions for times [0...n_timesteps-1] given all observations 時刻 t における状態変数の平滑化期待値 [時間軸,状態変数軸] V_smooth [n_time, n_dim_sys, n_dim_sys] {numpy-array, float} : covariances of hidden state distributions for times [0...n_timesteps-1] given all observations 時刻 t における状態変数の平滑化共分散 [時間軸,状態変数軸,状態変数軸] A [n_dim_sys, n_dim_sys] {numpy-array, float} : fixed interval smoothed gain 固定区間平滑化ゲイン [時間軸,状態変数軸,状態変数軸] ''' # filter が実行されていない場合は実行 try: self.x_pred[0] except: self.filter() T = self.y.shape[0] self.x_smooth = np.zeros((T, self.n_dim_sys), dtype=self.dtype) self.V_smooth = np.zeros((T, self.n_dim_sys, self.n_dim_sys), dtype=self.dtype) A = np.zeros((self.n_dim_sys, self.n_dim_sys), dtype=self.dtype) self.x_smooth[-1] = self.x_filt[-1] self.V_smooth[-1] = self.V_filt[-1] # t in [0, T-2] (tが1~Tの逆順であることに注意) for t in reversed(range(T - 1)): # 時間を可視化 print("\r smooth calculating... t={}".format(T - t) + "/" + str(T), end="") # 時刻 t のパラメータを取り出す F = _last_dims(self.F, t, 2) # 固定区間平滑ゲインの計算 A = np.dot(self.V_filt[t], np.dot(F.T, linalg.pinv(self.V_pred[t + 1]))) # 固定区間平滑化 self.x_smooth[t] = self.x_filt[t] \ + np.dot(A, self.x_smooth[t + 1] - self.x_pred[t + 1]) self.V_smooth[t] = self.V_filt[t] \ + np.dot(A, np.dot(self.V_smooth[t + 1] - self.V_pred[t + 1], A.T))
def _sigma_pair_smooth(self): ''' T {int} : length of y (時系列の長さ) V_pair [n_time, n_dim_sys, n_dim_sys] {numpy-array, float} : Covariance between hidden states at times t and t-1 for t = [1...n_timesteps-1]. Time 0 is ignored. 時刻t,t-1間の状態の共分散.0は無視する ''' # 時系列の長さ T = self.y.shape[0] self.x_smooth = np.zeros((T, self.n_dim_sys), dtype=self.dtype) self.V_smooth = np.zeros((T, self.n_dim_sys, self.n_dim_sys), dtype=self.dtype) # pairwise covariance self.V_pair = np.zeros((T, self.n_dim_sys, self.n_dim_sys), dtype=self.dtype) A = np.zeros((self.n_dim_sys, self.n_dim_sys), dtype=self.dtype) self.x_smooth[-1] = self.x_filt[-1] self.V_smooth[-1] = self.V_filt[-1] # t in [0, T-2] (tが1~Tの逆順であることに注意) for t in reversed(range(T - 1)): # 時間を可視化 print("\r expectation step calculating... t={}".format(T - t) + "/" + str(T), end="") # 時刻 t のパラメータを取り出す F = _last_dims(self.F, t, 2) # 固定区間平滑ゲインの計算 A = np.dot(self.V_filt[t], np.dot(F.T, linalg.pinv(self.V_pred[t + 1]))) # 固定区間平滑化 self.x_smooth[t] = self.x_filt[t] \ + np.dot(A, self.x_smooth[t + 1] - self.x_pred[t + 1]) self.V_smooth[t] = self.V_filt[t] \ + np.dot(A, np.dot(self.V_smooth[t + 1] - self.V_pred[t + 1], A.T)) # 時点間共分散行列 self.V_pair[t + 1] = np.dot(self.V_smooth[t], A.T)
def _predict_update_noise(self, t): if np.any(np.ma.getmask(self.y[t - 1])): self._predict_update_no_noise(t) else: # extract t-1 parameters (時刻t-1のパラメータ取り出す) F = _last_dims(self.F, t - 1, 2) Q = _last_dims(self.Q, t - 1, 2) b = _last_dims(self.b, t - 1, 1) H = _last_dims(self.H, t - 1, 2) d = _last_dims(self.d, t - 1, 1) S = _last_dims(self.S, t - 1, 2) R = _last_dims(self.R, t - 1, 2) # predict t distribution (時刻tの予測分布の計算) SR = S @ linalg.pinv(R) F_SRH = F - SR @ H self.x_pred[t] = F_SRH @ self.x_filt[t - 1] + b + SR @ ( self.y[t - 1] - d) self.V_pred[t] = F_SRH @ self.V_filt[t - 1] @ F_SRH.T + Q - SR @ S.T
def filter(self): ''' T {int} : 時系列の長さ,length of y x_pred_mean [n_time+1, n_dim_sys] {numpy-array, float} : mean of x_pred regarding to particles at time t 時刻 t における x_pred の粒子平均 [時間軸,状態変数軸] x_filt_mean [n_time+1, n_dim_sys] {numpy-array, float} : mean of x_filt regarding to particles 時刻 t における状態変数のフィルタ平均 [時間軸,状態変数軸] x_pred [n_dim_sys, n_particle] : hidden state at time t given observations for each particle 状態変数の予測アンサンブル [状態変数軸,粒子軸] x_filt [n_dim_sys, n_particle] {numpy-array, float} : hidden state at time t given observations for each particle 状態変数のフィルタアンサンブル [状態変数軸,粒子軸] w [n_particle] {numpy-array, float} : weight lambda of each particle 各粒子の尤度 [粒子軸] v [n_dim_sys, n_particle] {numpy-array, float} : system noise particles 各時刻の状態ノイズ [状態変数軸,粒子軸] ''' # 時系列の長さ, number of time-series data T = len(self.y) # initial filter, prediction self.x_pred_mean = np.zeros((T + 1, self.n_dim_sys), dtype=self.dtype) self.x_filt_mean = np.zeros((T + 1, self.n_dim_sys), dtype=self.dtype) x_pred = np.zeros((self.n_dim_sys, self.n_particle), dtype=self.dtype) # initial distribution # sys x particle x_filt = rd.multivariate_normal(self.initial_mean, self.initial_covariance, size=self.n_particle).T # initial setting self.x_pred_mean[0] = self.initial_mean self.x_filt_mean[0] = self.initial_mean for t in range(T): print("\r filter calculating... t={}".format(t), end="") ## filter update # 一期先予測, prediction f = _last_dims(self.f, t, 1)[0] # システムノイズをパラメトリックに発生, raise parametric system noise v = self.q[0](*self.q[1], size=self.n_particle).T # アンサンブル予測, ensemble prediction # sys x particle x_pred = f(*[x_filt, v]) # mean # sys self.x_pred_mean[t + 1] = np.mean(x_pred, axis=1) # 欠測値の対処, treat missing values if np.any(np.ma.getmask(self.y[t])): x_filt = x_pred self.x_filt_mean[t + 1] = self.x_pred_mean[t + 1] else: # log likelihood for each particle for y[t] # 対数尤度の計算 lf = self._last_likelihood(self.lf, t) lfp = self._last_likelihood(self.lfp, t) try: # particle w = lf(self.y[t], x_pred, *lfp) except: raise ValueError('you must check likelihood_functions' + 'and parameters.') # 発散防止, avoid evaporation if self.likelihood_function_is_log_form: w = np.exp(w - np.max(w)) else: w = w / np.max(w) # 重みの正規化, normalization # particle w = w / np.sum(w) # 重み付き平均の計算 # sys self.x_filt_mean[t + 1] = w @ x_pred.T # 重み付き共分散の計算 # sys x particle x_pred.T[:] -= self.x_filt_mean[t + 1] # sys x sys V_filt = np.dot(np.multiply(x_pred, w), x_pred.T) # sys x particle x_filt = rd.multivariate_normal(self.x_filt_mean[t + 1], V_filt, size=self.n_particle).T
def _calc_em(self, given={}): ''' T {int} : length of observation y ''' # length of y T = self.y.shape[0] # observation_matrices を最初に更新 if 'observation_matrices' not in given: '''math y_t : observation, d_t : observation_offsets x_t : system, H : observation_matrices H &= ( \sum_{t=0}^{T-1} (y_t - d_t) \mathbb{E}[x_t]^T ) ( \sum_{t=0}^{T-1} \mathbb{E}[x_t x_t^T] )^-1 ''' res1 = np.zeros((self.n_dim_obs, self.n_dim_sys), dtype=self.dtype) res2 = np.zeros((self.n_dim_sys, self.n_dim_sys), dtype=self.dtype) for t in range(T): # 欠測がない y_t に関して if not np.any(np.ma.getmask(self.y[t])): d = _last_dims(self.d, t, 1) # それぞれの要素毎の積を取りたいので,outer(外積)を使う res1 += np.outer(self.y[t] - d, self.x_smooth[t]) res2 += self.V_smooth[t] \ + np.outer(self.x_smooth[t], self.x_smooth[t]) # observation_matrices (H) を更新 self.H = np.dot(res1, linalg.pinv(res2)) # 次に observation_covariance を更新 if 'observation_covariance' not in given: '''math R : observation_covariance, H_t : observation_matrices, x_t : system, d_t : observation_offsets, y_t : observation R &= \frac{1}{T} \sum_{t=0}^{T-1} [y_t - H_t \mathbb{E}[x_t] - d_t] [y_t - H_t \mathbb{E}[x_t] - d_t]^T + H_t Var(x_t) H_t^T ''' # 計算補助 res1 = np.zeros((self.n_dim_obs, self.n_dim_obs), dtype=self.dtype) n_obs = 0 for t in range(T): if not np.any(np.ma.getmask(self.y[t])): H = _last_dims(self.H, t) d = _last_dims(self.d, t, 1) err = self.y[t] - np.dot(H, self.x_smooth[t]) - d res1 += np.outer(err, err) \ + np.dot(H, np.dot(self.V_smooth[t], H.T)) n_obs += 1 # temporary # tmp = self.R # 観測が1回でも確認できた場合 if n_obs > 0: self.R = (1.0 / n_obs) * res1 else: self.R = res1 # covariance_structure によって場合分け if self.observation_cs == 'triD1': # 新しい R を定義しておく new_R = np.zeros_like(self.R, dtype=self.dtype) # 対角成分に関して平均を取る np.fill_diagonal(new_R, self.R.diagonal().mean()) # 三重対角成分に関して平均を取る rho = (self.R.diagonal(1).mean() + self.R.diagonal(-1).mean()) / 2 # 結果の統合 self.R = new_R + np.diag(rho * np.ones(self.n_dim_obs - 1), 1) \ + np.diag(rho * np.ones(self.n_dim_obs - 1), -1) elif self.observation_cs == 'triD2': # 新しい R を定義しておく new_R = np.zeros_like(self.R, dtype=self.dtype) # 対角成分に関して平均を取る np.fill_diagonal(new_R, self.R.diagonal().mean()) # 三重対角成分, 隣接成分に関して平均を取る td = np.ones(self.n_dim_obs - 1) td[self.observation_v - 1::self.observation_v - 1] = 0 condition = np.diag(td, 1) + np.diag(td, -1) \ + np.diag( np.ones(self.n_dim_obs - self.observation_v), self.observation_v ) \ + np.diag( np.ones(self.n_dim_obs - self.observation_v), self.observation_v ) rho = self.R[condition.astype(bool)].mean() # 結果の統合 self.R = new_R + rho * condition.astype(self.dtype) # 次に transition_matrices の更新 if 'transition_matrices' not in given: '''math F : transition_matrices, x_t : system, b_t : transition_offsets F &= ( \sum_{t=1}^{T-1} \mathbb{E}[x_t x_{t-1}^{T}] - b_{t-1} \mathbb{E}[x_{t-1}]^T ) ( \sum_{t=1}^{T-1} \mathbb{E}[x_{t-1} x_{t-1}^T] )^{-1} ''' #計算補助 res1 = np.zeros((self.n_dim_sys, self.n_dim_sys), dtype=self.dtype) res2 = np.zeros((self.n_dim_sys, self.n_dim_sys), dtype=self.dtype) for t in range(1, T): b = _last_dims(self.b, t - 1, 1) res1 += self.V_pair[t] + np.outer(self.x_smooth[t], self.x_smooth[t - 1]) res1 -= np.outer(b, self.x_smooth[t - 1]) res2 += self.V_smooth[t - 1] \ + np.outer(self.x_smooth[t - 1], self.x_smooth[t - 1]) self.F = np.dot(res1, linalg.pinv(res2)) # 次に transition_covariance の更新 if 'transition_covariance' not in given: '''math Q : transition_covariance, x_t : system, b_t : transition_offsets, F_t : transition_matrices Q &= \frac{1}{T-1} \sum_{t=0}^{T-2} (\mathbb{E}[x_{t+1}] - A_t \mathbb{E}[x_t] - b_t) (\mathbb{E}[x_{t+1}] - A_t \mathbb{E}[x_t] - b_t)^T + F_t Var(x_t) F_t^T + Var(x_{t+1}) - Cov(x_{t+1}, x_t) F_t^T - F_t Cov(x_t, x_{t+1}) ''' # 計算補助 res1 = np.zeros((self.n_dim_sys, self.n_dim_sys), dtype=self.dtype) # 全てを最適化するわけではないので,素朴な計算になっている for t in range(T - 1): F = _last_dims(self.F, t) b = _last_dims(self.b, t, 1) err = self.x_smooth[t + 1] - np.dot(F, self.x_smooth[t]) - b Vt1t_F = np.dot(self.V_pair[t + 1], F.T) res1 += (np.outer(err, err) + np.dot(F, np.dot(self.V_smooth[t], F.T)) + self.V_smooth[t + 1] - Vt1t_F - Vt1t_F.T) self.Q = (1.0 / (T - 1)) * res1 # covariance_structure によって場合分け if self.transition_cs == 'triD1': # 新しい R を定義しておく new_Q = np.zeros_like(self.Q, dtype=self.dtype) # 対角成分に関して平均を取る np.fill_diagonal(new_Q, self.Q.diagonal().mean()) # 三重対角成分に関して平均を取る rho = (self.Q.diagonal(1).mean() + self.Q.diagonal(-1).mean()) / 2 # 結果の統合 self.Q = new_Q + np.diag(rho * np.ones(self.n_dim_sys - 1), 1)\ + np.diag(rho * np.ones(self.n_dim_sys - 1), -1) elif self.transition_cs == 'triD2': # 新しい R を定義しておく new_Q = np.zeros_like(self.Q, dtype=self.dtype) # 対角成分に関して平均を取る np.fill_diagonal(new_Q, self.Q.diagonal().mean()) # 三重対角成分, 隣接成分に関して平均を取る td = np.ones(self.n_dim_sys - 1) td[self.transition_v - 1::self.transition_v - 1] = 0 condition = np.diag(td, 1) + np.diag(td, -1) \ + np.diag( np.ones(self.n_dim_sys - self.transition_v), self.transition_v ) \ + np.diag( np.ones(self.n_dim_sys - self.transition_v), self.transition_v ) rho = self.Q[condition.astype(bool)].mean() # 結果の統合 self.Q = new_Q + rho * condition.astype(self.dtype) # 次に initial_mean の更新 if 'initial_mean' not in given: '''math x_0 : system of t=0 \mu_0 = \mathbb{E}[x_0] ''' tmp = self.initial_mean self.initial_mean = self.x_smooth[0] # 次に initial_covariance の更新 if 'initial_covariance' not in given: '''math mu_0 : system of t=0 \Sigma_0 = \mathbb{E}[x_0, x_0^T] - \mu_0 \mu_0^T ''' x0 = self.x_smooth[0] x0_x0 = self.V_smooth[0] + np.outer(x0, x0) self.initial_covariance = x0_x0 - np.outer(self.initial_mean, x0) self.initial_covariance += - np.outer(x0, self.initial_mean)\ + np.outer(self.initial_mean, self.initial_mean) # 次に transition_offsets の更新 if 'transition_offsets' not in given: '''math b : transition_offsets, x_t : system F_t : transition_matrices b = \frac{1}{T-1} \sum_{t=1}^{T-1} \mathbb{E}[x_t] - F_{t-1} \mathbb{E}[x_{t-1}] ''' self.b = np.zeros(self.n_dim_sys, dtype=self.dtype) # 最低でも3点での値が必要 if T > 1: for t in range(1, T): F = _last_dims(self.F, t - 1) self.b += self.x_smooth[t] - np.dot( F, self.x_smooth[t - 1]) self.b *= (1.0 / (T - 1)) # 最後に observation_offsets の更新 if 'observation_offsets' not in given: '''math d : observation_offsets, y_t : observation H_t : observation_matrices, x_t : system d = \frac{1}{T} \sum_{t=0}^{T-1} y_t - H_{t} \mathbb{E}[x_{t}] ''' self.d = np.zeros(self.n_dim_obs, dtype=self.dtype) n_obs = 0 for t in range(T): if not np.any(np.ma.getmask(self.y[t])): H = _last_dims(self.H, t) self.d += self.y[t] - np.dot(H, self.x_smooth[t]) n_obs += 1 if n_obs > 0: self.d *= (1.0 / n_obs)
def filter(self): ''' T {int} : length of data y (時系列の長さ) x_pred [n_time, n_dim_sys] {numpy-array, float} : mean of hidden state at time t given observations from times [0...t-1] 時刻 t における状態変数の予測期待値 [時間軸,状態変数軸] V_pred [n_time, n_dim_sys, n_dim_sys] {numpy-array, float} : covariance of hidden state at time t given observations from times [0...t-1] 時刻 t における状態変数の予測共分散 [時間軸,状態変数軸,状態変数軸] x_filt [n_time, n_dim_sys] {numpy-array, float} : mean of hidden state at time t given observations from times [0...t] 時刻 t における状態変数のフィルタ期待値 [時間軸,状態変数軸] V_filt [n_time, n_dim_sys, n_dim_sys] {numpy-array, float} : covariance of hidden state at time t given observations from times [0...t] 時刻 t における状態変数のフィルタ共分散 [時間軸,状態変数軸,状態変数軸] K [n_dim_sys, n_dim_obs] {numpy-array, float} : Kalman gain matrix for time t [状態変数軸,観測変数軸] カルマンゲイン ''' T = self.y.shape[0] self.x_pred = np.zeros((T, self.n_dim_sys), dtype=self.dtype) self.V_pred = np.zeros((T, self.n_dim_sys, self.n_dim_sys), dtype=self.dtype) self.x_filt = np.zeros((T, self.n_dim_sys), dtype=self.dtype) self.V_filt = np.zeros((T, self.n_dim_sys, self.n_dim_sys), dtype=self.dtype) K = np.zeros((self.n_dim_sys, self.n_dim_obs), dtype=self.dtype) # 各時刻で予測・フィルタ計算 for t in range(T): # 計算している時間を可視化 print("\r filter calculating... t={}".format(t) + "/" + str(T), end="") if t == 0: # initial setting (初期分布) self.x_pred[0] = self.initial_mean self.V_pred[0] = self.initial_covariance else: self.predict_update(t) # y[t] の何れかがマスク処理されていれば,フィルタリングはカットする if np.any(np.ma.getmask(self.y[t])): self.x_filt[t] = self.x_pred[t] self.V_filt[t] = self.V_pred[t] else: # extract t parameters (時刻tのパラメータを取り出す) H = _last_dims(self.H, t, 2) R = _last_dims(self.R, t, 2) d = _last_dims(self.d, t, 1) # filtering (フィルタ分布の計算) K = self.V_pred[t] @ ( H.T @ linalg.pinv(H @ (self.V_pred[t] @ H.T + R))) self.x_filt[t] = self.x_pred[t] + K @ ( self.y[t] - (H @ self.x_pred[t] + d)) self.V_filt[t] = self.V_pred[t] - K @ (H @ self.V_pred[t])
def smooth(self, lag=10): ''' lag {int} : ラグ,lag of smoothing T {int} : 時系列の長さ,length of y x_pred_mean [n_time+1, n_dim_sys] {numpy-array, float} : mean of x_pred regarding to particles at time t 時刻 t における x_pred の粒子平均 [時間軸,状態変数軸] x_filt_mean [n_time+1, n_dim_sys] {numpy-array, float} : mean of x_filt regarding to particles at time t 時刻 t における状態変数のフィルタ平均 [時間軸,状態変数軸] x_smooth_mean [n_time, n_dim_sys] {numpy-array, float} : mean of x_smooth regarding to particles at time t 時刻 t における状態変数の平滑化平均 [時間軸,状態変数軸] x_pred [n_dim_sys, n_particle] : hidden state at time t given observations[:t-1] for each particle 状態変数の予測アンサンブル [状態変数軸,粒子軸] x_filt [n_particle, n_dim_sys] {numpy-array, float} : hidden state at time t given observations[:t] for each particle 状態変数のフィルタアンサンブル [状態変数軸,粒子軸] x_smooth [n_time, n_dim_sys, n_particle] {numpy-array, float} : hidden state at time t given observations[:t+lag] for each particle 状態変数の平滑化アンサンブル [時間軸,状態変数軸,粒子軸] w [n_particle] {numpy-array, float} : weight lambda of each particle 各粒子の尤度 [粒子軸] v [n_dim_sys, n_particle] {numpy-array, float} : system noise particles 各時刻の状態ノイズ [状態変数軸,粒子軸] k [n_particle] {numpy-array, float} : index number for resampling 各時刻のリサンプリングインデックス [粒子軸] ''' # 時系列の長さ, number of time-series data T = len(self.y) # initial filter, prediction self.x_pred_mean = np.zeros((T + 1, self.n_dim_sys), dtype=self.dtype) self.x_filt_mean = np.zeros((T + 1, self.n_dim_sys), dtype=self.dtype) self.x_smooth_mean = np.zeros((T + 1, self.n_dim_sys), dtype=self.dtype) x_pred = np.zeros((self.n_dim_sys, self.n_particle), dtype=self.dtype) x_filt = np.zeros((self.n_dim_sys, self.n_particle), dtype=self.dtype) x_smooth = np.zeros((T + 1, self.n_dim_sys, self.n_particle), dtype=self.dtype) # initial distribution x_filt = rd.multivariate_normal(self.initial_mean, self.initial_covariance, size=self.n_particle).T # initial setting self.x_pred_mean[0] = self.initial_mean self.x_filt_mean[0] = self.initial_mean self.x_smooth_mean[0] = self.initial_mean for t in range(T): print("\r filter and smooth calculating... t={}".format(t), end="") ## filter update # 一期先予測, prediction f = _last_dims(self.f, t, 1)[0] # システムノイズをパラメトリックに発生, raise parametric system noise v = self.q[0](*self.q[1], size=self.n_particle).T # アンサンブル予測, ensemble prediction x_pred = f(*[x_filt, v]) # mean self.x_pred_mean[t + 1] = np.mean(x_pred, axis=1) # 欠測値の対処, treat missing values if np.any(np.ma.getmask(self.y[t])): x_filt = x_pred else: # log likelihood for each particle for y[t] # 対数尤度の計算 lf = self._last_likelihood(self.lf, t) lfp = self._last_likelihood(self.lfp, t) try: w = lf(self.y[t], x_pred, *lfp) except: raise ValueError('you must check likelihood_functions' + ' and parameters.') # 発散防止, avoid evaporation if self.likelihood_function_is_log_form: w = np.exp(w - np.max(w)) else: w = w / np.max(w) # resampling k = self._stratified_resampling(w) x_filt = x_pred[:, k] # regularization if self.regularization: x_filt += self.eta[0](*self.eta[1], size=self.n_particle).T # initial smooth value x_smooth[t + 1] = x_filt # mean self.x_filt_mean[t + 1] = np.mean(x_filt, axis=1) # smoothing if (t > lag - 1): x_smooth[t - lag:t + 1] = x_smooth[t - lag:t + 1, :, k] else: x_smooth[:t + 1] = x_smooth[:t + 1, :, k] # x_smooth_mean self.x_smooth_mean = np.mean(x_smooth, axis=2)
def filter(self): ''' T {int} : length of data y (時系列の長さ) x_pred_mean [n_time+1, n_dim_sys] {numpy-array, float} : mean of x_pred regarding to particles at time t 時刻 t における x_pred の粒子平均 [時間軸,状態変数軸] V_pred [n_time+1, n_dim_sys, n_dim_sys] {numpy-array, float} : covariance of hidden state at time t given observations from times [0...t-1] 時刻 t における状態変数の予測共分散 [時間軸,状態変数軸,状態変数軸] x_filt [n_time+1, n_particle, n_dim_sys] {numpy-array, float} : hidden state at time t given observations for each particle 状態変数のフィルタアンサンブル [時間軸,粒子軸,状態変数軸] x_filt_mean [n_time+1, n_dim_sys] {numpy-array, float} : mean of x_filt regarding to particles 時刻 t における状態変数のフィルタ平均 [時間軸,状態変数軸] X5 [n_time, n_dim_sys, n_dim_obs] {numpy-array, float} : right operator for filter, smooth calulation filter, smoothing 計算で用いる各時刻の右作用行列 x_pred [n_particle, n_dim_sys] {numpy-array, float} : hidden state at time t given observations for each particle 状態変数の予測アンサンブル [粒子軸,状態変数軸] x_pred_center [n_particle, n_dim_sys] {numpy-array, float} : centering of x_pred x_pred の中心化 [粒子軸,状態変数軸] w_ensemble [n_particle, n_dim_obs] {numpy-array, float} : observation noise ensemble 観測ノイズのアンサンブル [粒子軸,観測変数軸] Inovation [n_dim_obs, n_particle] {numpy-array, float} : Inovation from observation [観測変数軸,粒子軸] 観測と予測のイノベーション ''' # 時系列の長さ, lenght of time-series T = self.y.shape[0] ## 配列定義, definition of array # 時刻0における予測・フィルタリングは初期値, initial setting self.x_pred_mean = np.zeros((T + 1, self.n_dim_sys), dtype=self.dtype) self.x_filt = np.zeros((T + 1, self.n_particle, self.n_dim_sys), dtype=self.dtype) self.x_filt[0, :] = self.initial_mean self.x_filt_mean = np.zeros((T + 1, self.n_dim_sys), dtype=self.dtype) self.X5 = np.zeros((T + 1, self.n_particle, self.n_particle), dtype=self.dtype) self.X5[:] = np.eye(self.n_particle, dtype=self.dtype) # 初期値のセッティング, initial setting self.x_pred_mean[0] = self.initial_mean self.x_filt_mean[0] = self.initial_mean # initial setting x_pred = np.zeros((self.n_particle, self.n_dim_sys), dtype=self.dtype) x_pred_center = np.zeros((T + 1, self.n_particle, self.n_dim_sys), dtype=self.dtype) w_ensemble = np.zeros((self.n_particle, self.n_dim_obs), dtype=self.dtype) # イノベーション, observation inovation Inovation = np.zeros((self.n_dim_obs, self.n_particle), dtype=self.dtype) # 各時刻で予測・フィルタ計算, prediction and filtering for t in range(T): # 計算している時間を可視化, visualization for calculation print("\r filter calculating... t={}".format(t + 1) + "/" + str(T), end="") ## filter update # 一期先予測, prediction f = _last_dims(self.f, t, 1)[0] # システムノイズをパラメトリックに発生, raise parametric system noise v = self.q[0](*self.q[1], size=self.n_particle) # アンサンブル予測, ensemble prediction x_pred = f(*np.transpose([self.x_filt[t], v], (0, 2, 1))).T # x_pred_mean を計算, calculate x_pred_mean self.x_pred_mean[t + 1] = np.mean(x_pred, axis=0) # 欠測値の対処, treat missing values if np.any(np.ma.getmask(self.y[t])): self.x_filt[t + 1] = x_pred else: # x_pred_center を計算, calculate x_pred_center x_pred_center = x_pred - self.x_pred_mean[t + 1] # 観測ノイズのアンサンブルを発生, raise observation noise ensemble R = _last_dims(self.R, t) w_ensemble = rd.multivariate_normal(np.zeros(self.n_dim_obs), R, size=self.n_particle) # イノベーションの計算 H = _last_dims(self.H, t) Inovation.T[:] = self.y[t] Inovation += w_ensemble.T - H @ x_pred.T # 特異値分解 U, s, _ = linalg.svd(H @ x_pred_center.T + w_ensemble.T, False) # 右作用行列の計算 X1 = np.diag(1 / (s * s)) @ U.T X2 = X1 @ Inovation X3 = U @ X2 X4 = (H @ x_pred_center.T).T @ X3 self.X5[t + 1] = np.eye(self.n_particle, dtype=self.dtype) + X4 # フィルタ分布のアンサンブルメンバーの計算 self.x_filt[t + 1] = self.X5[t + 1].T @ x_pred # フィルタ分布のアンサンブル平均の計算 self.x_filt_mean[t + 1] = np.mean(self.x_filt[t + 1], axis=0)
def filter(self): ''' T {int} : length of data y (時系列の長さ) <class variables> x_pred_mean [n_time+1, n_dim_sys] {numpy-array, float} : mean of x_pred regarding to particles at time t 時刻 t における x_pred の粒子平均 [時間軸,状態変数軸] x_filt [n_time+1, n_dim_sys, n_particle] {numpy-array, float} : hidden state at time t given observations for each particle 状態変数のフィルタアンサンブル [時間軸,状態変数軸,粒子軸] x_filt_mean [n_time+1, n_dim_sys] {numpy-array, float} : mean of x_filt regarding to particles 時刻 t における状態変数のフィルタ平均 [時間軸,状態変数軸] <global variables> x_pred [n_dim_sys, n_particle] {numpy-array, float} : hidden state at time t given observations for each particle 状態変数の予測アンサンブル [状態変数軸,粒子軸] x_pred_center [n_dim_sys, n_particle] {numpy-array, float} : centering of x_pred x_pred の中心化 [状態変数軸,粒子軸] v [n_dim_sys, n_particle] {numpy-array, float} : system noise for transition システムノイズ [状態変数軸,粒子軸] y_background [n_dim_obs, n_particle] {numpy-array, float} : background value of observation space 観測空間における背景値(=一気先予測値) [観測変数軸,粒子軸] y_background_mean [n_dim_obs] {numpy-array, float} : mean of y_background for particles y_background の粒子平均 [観測変数軸] y_background_center [n_dim_obs, n_particle] {numpy-array, float} : centerized value of y_background for particles y_background の粒子中心化 [観測変数軸,粒子軸] <local variables> x_pred_mean_local [n_dim_local] {numpy-array, float} : localization from x_pred_mean x_pred_mean の局所値 [局所変数軸] x_pred_center_local [n_dim_local, n_particle] {numpy-array, float} : localization from x_pred_center x_pred_center の局所値 [局所変数軸,粒子軸] y_background_mean_local [n_dim_local] {numpy-array, float} : localization from y_background_mean y_background_mean の局所値 [局所変数軸] y_background_center_local [n_dim_local, n_particle] {numpy-array, float} : localization from y_background_center y_background_center の局所値 [局所変数軸,粒子軸] y_local [n_dim_local] {numpy-array, float} : localization from observation y 観測 y の局所値 [局所変数軸] R_local [n_dim_local, n_dim_local] {numpy-array, float} : localization from observation covariance R 観測共分散 R の局所値 [局所変数軸,局所変数軸] ''' # 時系列の長さ, lenght of time-series T = self.y.shape[0] ## 配列定義, definition of array # 時刻0における予測・フィルタリングは初期値, initial setting self.x_pred_mean = np.zeros((T + 1, self.n_dim_sys), dtype=self.dtype) self.x_filt = np.zeros((T + 1, self.n_dim_sys, self.n_particle), dtype=self.dtype) self.x_filt[0].T[:] = self.initial_mean self.x_filt_mean = np.zeros((T + 1, self.n_dim_sys), dtype=self.dtype) # 初期値のセッティング, initial setting self.x_pred_mean[0] = self.initial_mean self.x_filt_mean[0] = self.initial_mean # 各時刻で予測・フィルタ計算, prediction and filtering for t in range(T): # 計算している時間を可視化, visualization for calculation print('\r filter calculating... t={}'.format(t + 1) + '/' + str(T), end='') ## filter update # 一期先予測, prediction f = _last_dims(self.f, t, 1)[0] # システムノイズをパラメトリックに発生, raise parametric system noise # sys x particle v = self.q[0](*self.q[1], size=self.n_particle).T # アンサンブル予測, ensemble prediction # sys x particle x_pred = f(*[self.x_filt[t], v]) # x_pred_mean を計算, calculate x_pred_mean # time x sys self.x_pred_mean[t + 1] = np.mean(x_pred, axis=1) # 欠測値の対処, treat missing values if np.any(np.ma.getmask(self.y[t])): # time x sys x particle self.x_filt[t + 1] = x_pred else: ## Step1 : model space -> observation space h = _last_dims(self.h, t, 1)[0] R = _last_dims(self.R, t) # y_background : obs x particle y_background = h(x_pred) # y_background mean : obs y_background_mean = np.mean(y_background, axis=1) # y_background center : obs x particle y_background_center = (y_background.T - y_background_mean).T ## Step2 : calculate for model space # x_pred_center : sys x particle x_pred_center = (x_pred.T - self.x_pred_mean[t + 1]).T ## Step3 : select data for grid point # global を local に移す写像(並列処理) # n_dim_sys x local_sys x_pred_mean_local = self._parallel_global_to_local( self.x_pred_mean[t], self.n_dim_sys, self.A_sys) # n_dim_sys x local_sys x particle x_pred_center_local = self._parallel_global_to_local( x_pred_center, self.n_dim_sys, self.A_sys) # n_dim_sys x local_obs y_background_mean_local = self._parallel_global_to_local( y_background_mean, self.n_dim_obs, self.A_obs) # n_dim_sys x local_obs x particle y_background_center_local = self._parallel_global_to_local( y_background_center, self.n_dim_obs, self.A_obs) # n_dim_sys x local_obs y_local = self._parallel_global_to_local( self.y[t], self.n_dim_obs, self.A_obs) # n_dim_sys x local_obs x local_obs R_local = np.zeros(self.n_dim_sys, dtype=object) for i in range(self.n_dim_sys): R_local[i] = R[self.A_obs[i]][:, self.A_obs[i]] ## Step4-9 : local processing p = Pool(self.cpu_number) self.x_filt[t + 1] = p.starmap( local_multi_processing, zip(x_pred_mean_local, x_pred_center_local, y_background_mean_local, y_background_center_local, y_local, R_local, range(self.n_dim_sys), self.A_sys, itertools.repeat(self.n_particle), itertools.repeat(self.rho))) p.close() # フィルタ分布のアンサンブル平均の計算 self.x_filt_mean[t + 1] = np.mean(self.x_filt[t + 1], axis=1)