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
Example #2
0
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
Example #3
0
    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
Example #4
0
    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 = {}
Example #5
0
    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
Example #7
0
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)