def step(self): """ Description: Moves the system dynamics one time-step forward. Args: None Returns: The next value in the ARMA time-series. """ assert self.initialized self.T += 1 if(self.T == 5000): self.d = 0 self.delta_i_x = None if(self.noise_list is not None): self.x, self.delta_i_x, self.noise, x_new = self._step(self.x, self.delta_i_x, self.noise, self.noise_list[self.q + self.T - 1]) else: if(self.noise_distribution == 'normal'): self.x,self.delta_i_x, self.noise, x_new = self._step(self.x, self.delta_i_x,self.noise, \ self.noise_magnitude * random.normal(generate_key(), shape=(self.n,))) elif(self.noise_distribution == 'unif'): self.x, self.delta_i_x, self.noise, x_new = self._step(self.x, self.delta_i_x,self.noise, \ self.noise_magnitude * random.uniform(generate_key(), shape=(self.n,), minval=-1., maxval=1.)) return x_new
def test_rnn(steps=100, show_plot=True): T = steps p, q = 3, 3 n = 1 problem = tigerforecast.problem("ARMA-v0") y_true = problem.initialize(p=p, q=q, n=1) method = tigerforecast.method("RNN") method.initialize(n=1, m=1, l=3, h=1) loss = lambda pred, true: np.sum((pred - true)**2) results = [] for i in range(T): u = random.normal(generate_key(), (n, )) y_pred = method.predict(u) y_true = problem.step() results.append(loss(y_true, y_pred)) method.update(y_true) if show_plot: plt.plot(results) plt.title("RNN method on LDS problem") plt.show(block=False) plt.pause(3) plt.close() print("test_rnn passed") return
def step(self): """ Description: Moves the system dynamics one time-step forward. Args: u (numpy.ndarray): control input, an n-dimensional real-valued vector. Returns: A new observation from the LDS. """ assert self.initialized self.T += 1 x = random.normal(generate_key(), shape=(self.n, )) self.h, y = self._step( x, self.h, (random.normal(generate_key(), shape=(self.d, )), random.normal(generate_key(), shape=(self.m, )))) return x, y
def initialize(self, n, m, k, T, eta, R_M): """ Description: Initialize the (non-existent) hidden dynamics of the method Args: None Returns: None """ self.initialized = True self.n, self.m, self.k, self.T = n, m, k, T self.eta, self.R_M = eta, R_M self.k_prime = n * k + 2 * n + m self.M = rand.uniform(generate_key(), shape=(self.m, self.k_prime)) if (4 * k > T): raise Exception("Method parameter k must be less than T/4") self.X = np.zeros((n, T)) self.Y = np.zeros((m, T)) self.X_sim = None self.t = 0 self.y_hat = None self.k_values, self.k_vectors = self.eigen_pairs(T, k) self.eigen_diag = np.diag(self.k_values**0.25) @jax.jit def _update_x(X, x): new_x = np.roll(X, 1, axis=1) new_x = jax.ops.index_update(new_x, jax.ops.index[:, 0], x) return new_x self._update_x = _update_x self.params = {}
def initialize(self, n, m, d, noise=1.0): """ Description: Randomly initialize the hidden dynamics of the system. Args: n (int): Input dimension. m (int): Observation/output dimension. d (int): Hidden state dimension. noise (float): Default value 1.0. The magnitude of the noise (Gaussian) added to both the hidden state and the observable output. Returns: The first value in the time-series """ self.initialized = True self.T = 0 self.max_T = -1 self.has_regressors = True self.n, self.m, self.d, self.noise = n, m, d, noise # shrinks matrix M such that largest eigenvalue has magnitude k normalize = lambda M, k: k * M / np.linalg.norm(M, ord=2) # initialize matrix dynamics self.A = random.normal(generate_key(), shape=(d, d)) self.B = random.normal(generate_key(), shape=(d, n)) self.C = random.normal(generate_key(), shape=(m, d)) self.D = random.normal(generate_key(), shape=(m, n)) self.h = random.normal(generate_key(), shape=(d, )) # adjust dynamics matrix A self.A = normalize(self.A, 1.0) self.B = normalize(self.B, 1.0) self.C = normalize(self.C, 1.0) self.D = normalize(self.D, 1.0) def _step(u, h, eps): eps_h, eps_y = eps next_h = np.dot(self.A, h) + np.dot(self.B, u) + self.noise * eps_h y = np.dot(self.C, next_h) + np.dot(self.D, u) + self.noise * eps_y return (next_h, y) self._step = jax.jit(_step) return self.step()
def initialize(self, params = {'T': 10000}, p=3, q=3, n = 1, d=2, noise_list = None, c=0, noise_magnitude=0.1, noise_distribution = 'normal'): """ Description: Randomly initialize the hidden dynamics of the system. Args: p (int/numpy.ndarray): Autoregressive dynamics. If type int then randomly initializes a Gaussian length-p vector with L1-norm bounded by 1.0. If p is a 1-dimensional numpy.ndarray then uses it as dynamics vector. q (int/numpy.ndarray): Moving-average dynamics. If type int then randomly initializes a Gaussian length-q vector (no bound on norm). If p is a 1-dimensional numpy.ndarray then uses it as dynamics vector. n (int): Dimension of values. c (float): Default value follows a normal distribution. The ARMA dynamics follows the equation x_t = c + AR-part + MA-part + noise, and thus tends to be centered around mean c. Returns: The first value in the time-series """ self.initialized = True self.T = 0 self.max_T = params['T'] self.n = n self.d = d if type(p) == int: phi = random.normal(generate_key(), shape=(p,)) self.phi = 0.99 * phi / np.linalg.norm(phi, ord=1) else: self.phi = p if type(q) == int: self.psi = random.normal(generate_key(), shape=(q,)) else: self.psi = q if(type(self.phi) is list): self.p = self.phi[0].shape[0] else: self.p = self.phi.shape[0] if(type(self.psi) is list): self.q = self.psi[0].shape[0] else: self.q = self.psi.shape[0] self.noise_magnitude, self.noise_distribution = noise_magnitude, noise_distribution self.c = random.normal(generate_key(), shape=(self.n,)) if c == None else c self.x = random.normal(generate_key(), shape=(self.p, self.n)) if self.d>1: self.delta_i_x = random.normal(generate_key(), shape=(self.d-1, self.n)) else: self.delta_i_x = None self.noise_list = None if(noise_list is not None): self.noise_list = noise_list self.noise = np.array(noise_list[0:self.q]) elif(noise_distribution == 'normal'): self.noise = self.noise_magnitude * random.normal(generate_key(), shape=(self.q, self.n)) elif(noise_distribution == 'unif'): self.noise = self.noise_magnitude * random.uniform(generate_key(), shape=(self.q, self.n), \ minval=-1., maxval=1.) self.feedback=0.0 self.x_new = self.x[0] def _step(x, delta_i_x, noise, eps): if(type(self.phi) is list): x_ar = np.dot(x.T, self.phi[self.T]) else: x_ar = np.dot(x.T, self.phi) if(type(self.psi) is list): x_ma = np.dot(noise.T, self.psi[self.T]) else: x_ma = np.dot(noise.T, self.psi) if delta_i_x is not None: x_delta_sum = np.sum(delta_i_x) else : x_delta_sum = 0.0 x_delta_new=self.c + x_ar + x_ma + eps x_new = x_delta_new+x_delta_sum next_x = np.roll(x, self.n) next_noise = np.roll(noise, self.n) next_x = jax.ops.index_update(next_x, 0, x_delta_new) # equivalent to self.x[0] = self.x_new next_noise = jax.ops.index_update(next_noise, 0, eps) # equivalent to self.noise[0] = eps next_delta_i_x=None for i in range(d-1): if i==0: next_delta_i_x=jax.ops.index_update(delta_i_x, i, x_delta_new+delta_i_x[i]) else: next_delta_i_x=jax.ops.index_update(delta_i_x, i, next_delta_i_x[i-1]+next_delta_i_x[i]) return (next_x, next_delta_i_x, next_noise, x_new) self._step = jax.jit(_step) if self.delta_i_x is not None: x_delta_sum= np.sum(self.delta_i_x) else: x_delta_sum= 0 return self.x[0]+x_delta_sum
def test_wave_filtering(show_plot=False): # state variables T, n, m = 1000, 10, 10 # method variables k, eta = 20, 0.00002 # update_steps is an optional variable recommended by Yi R_Theta = 5 R_M = 2 * R_Theta * R_Theta * k**0.5 # generate random data (columns of Y) hidden_state_dim = 5 h = rand.uniform(generate_key(), minval=-1, maxval=1, shape=(hidden_state_dim, )) # first hidden state h_0 A = rand.normal(generate_key(), shape=(hidden_state_dim, hidden_state_dim)) B = rand.normal(generate_key(), shape=(hidden_state_dim, n)) C = rand.normal(generate_key(), shape=(m, hidden_state_dim)) D = rand.normal(generate_key(), shape=(m, n)) A = (A + A.T) / 2 # make A symmetric A = 0.99 * A / np.linalg.norm(A, ord=2) if (np.linalg.norm(B) > R_Theta): B = B * (R_Theta / np.linalg.norm(B)) if (np.linalg.norm(C) > R_Theta): C = C * (R_Theta / np.linalg.norm(C)) if (np.linalg.norm(D) > R_Theta): D = D * (R_Theta / np.linalg.norm(D)) if (np.linalg.norm(h) > R_Theta): h = h * (R_Theta / np.linalg.norm(h)) # input vectors are random data X = rand.normal(generate_key(), shape=(n, T)) # generate Y according to predetermined matrices Y = [] for t in range(T): Y.append( C.dot(h) + D.dot(X[:, t]) + rand.truncated_normal(generate_key(), 0, 0.1, shape=(m, ))) h = A.dot(h) + B.dot(X[:, t]) + rand.truncated_normal( generate_key(), 0, 0.1, shape=(hidden_state_dim, )) Y = np.array(Y).T # list to numpy matrix method = tigerforecast.method("WaveFiltering") method.initialize(n, m, k, T, eta, R_M) # loss = lambda y_true, y_pred: (y_true - y_pred)**2 loss = lambda y_true, y_pred: (np.linalg.norm(y_true - y_pred))**2 lastvalue_method = tigerforecast.method("LastValue") lastvalue_method.initialize() results = [] lastvalue_results = [] for i in range(T): # print(i) cur_y_pred = method.predict(X[:, i]) #print(method.forecast(cur_x, 3)) cur_y_true = Y[:, i] cur_loss = loss(cur_y_true, cur_y_pred) results.append(cur_loss) method.update(cur_y_true) lastvalue_cur_y_pred = lastvalue_method.predict(X[:, i]) lastvalue_cur_y_true = Y[:, i] lastvalue_cur_loss = loss(lastvalue_cur_y_true, lastvalue_cur_y_pred) lastvalue_results.append(lastvalue_cur_loss) # print("test_wave_filtering passed") print(np.linalg.norm(X[:, -1])) print(np.linalg.norm(Y[:-1])) print(results[-10:-1]) if show_plot: plt.plot(results) plt.title("WaveFiltering method on random data") plt.show(block=True) plt.plot(lastvalue_results) plt.title("LastValue method on random data") plt.show(block=True)