def __init__(self, X, Y, U, phi=observables.monomials(2), psi=observables.monomials(2), regressor='ols', is_generator=False, p_inv=True): self.X = X self.Y = Y self.U = U self.phi = phi self.psi = psi # Get number of data points self.N = self.X.shape[1] # Construct Phi and Psi matrices self.Phi_X = self.phi(X) self.Phi_Y = self.phi(Y) self.Psi_U = self.psi(U) # TODO: Create Koopman Generator Tensor if is_generator is True # Get dimensions self.dim_phi = self.Phi_X.shape[0] self.dim_psi = self.Psi_U.shape[0] # Make sure data is full rank # checkMatrixRank(self.Phi_X, "Phi_X") # checkMatrixRank(self.Phi_Y, "Phi_Y") # checkMatrixRank(self.Psi_U, "Psi_U") # Make sure condition numbers are small # checkConditionNumber(self.Phi_X, "Phi_X") # checkConditionNumber(self.Phi_Y, "Phi_Y") # checkConditionNumber(self.Psi_U, "Psi_U") # Build matrix of kronecker products between u_i and x_i for all 0 <= i <= N self.kronMatrix = np.empty([self.dim_psi * self.dim_phi, self.N]) for i in range(self.N): self.kronMatrix[:, i] = np.kron(self.Psi_U[:, i], self.Phi_X[:, i]) # Solve for M and B if regressor == 'rrr': self.M = estimate_L.rrr(self.kronMatrix.T, self.Phi_Y.T).T self.B = estimate_L.rrr(self.Phi_X.T, self.X.T) if regressor == 'sindy': self.M = estimate_L.SINDy(self.kronMatrix.T, self.Phi_Y.T).T self.B = estimate_L.SINDy(self.Phi_X.T, self.X.T) else: self.M = estimate_L.ols(self.kronMatrix.T, self.Phi_Y.T, p_inv).T self.B = estimate_L.ols(self.Phi_X.T, self.X.T, p_inv) # reshape M into tensor K self.K = np.empty([self.dim_phi, self.dim_phi, self.dim_psi]) for i in range(self.dim_phi): self.K[i] = self.M[i].reshape([self.dim_phi, self.dim_psi], order='F')
def SINDy(X, eps=0.001, iterations=10): m = X.shape[1] psi = observables.monomials(m) PsiX = psi(X) # not sure what this is supposed to be Xi = Y @ _sp.linalg.pinv(PsiX) # least-squares initial guess for k in range(iterations): s = abs(Xi) < eps # find coefficients less than eps ... Xi[s] = 0 # ... and set them to zero for ind in range(m): # for each snapshot b = ~s[ind, :] # consider only functions corresponding to coefficients greater than eps # '~' operator flips T/F of s # if arr = [1,2] then arr[[False, True]] == [2] Xi[ind, b] = Y[ind, :] @ _sp.linalg.pinv(PsiX[b, :]) return Xi
import observables import tensorflow as tf import tf_observables x = tf.stack([[1], [5]]) order = 3 phi = observables.monomials(order) tf_phi = tf_observables.monomials(order) tf.assert_equal(tf.cast(phi(x), tf.float32), tf_phi(x))
N = 10000 action_range = 10 state_range = 10 U = np.random.rand(1, N) * action_range * np.random.choice(np.array([-1, 1]), size=(1, N)) X0 = np.random.rand(2, N) * state_range * np.random.choice(np.array([-1, 1]), size=(2, N)) #%% Construct snapshots of states following dynamics f Y = f(X0, U) #%% Estimate Koopman tensor tensor = KoopmanTensor(X0, Y, U, phi=observables.monomials(1), psi=observables.monomials(1), regressor='sindy') #%% Training error norms = np.empty((N)) for i in range(N): phi_x = np.vstack(tensor.Phi_X[:, i]) # current (lifted) state action = np.vstack(tensor.U[:, i]) # true_x_prime = np.vstack(tensor.Y[:,i]) true_phi_x_prime = np.vstack(tensor.Phi_Y[:, i]) predicted_phi_x_prime = tensor.K_(action) @ phi_x # Compute norms
Z = np.roll(X,-1)[:, :-1] X = X[:, :-1] #%% '''======================= SETUP/DEFINITIONS =======================''' import observables from sympy import symbols from sympy.polys.monomials import itermonomials, monomial_count from sympy.polys.orderings import monomial_key d = X.shape[0] m = X.shape[1] s = int(d*(d+1)/2) # number of second order poly terms rtoler=1e-02 atoler=1e-02 psi = observables.monomials(2) #%% x_str = "" for i in range(d): x_str += 'x_' + str(i) + ', ' x_syms = symbols(x_str) M = itermonomials(x_syms, 2) sortedM = sorted(M, key=monomial_key('grlex', np.flip(x_syms))) # print(sortedM) #%% Psi_X = psi(X) Psi_X_T = Psi_X.T nablaPsi = psi.diff(X) nabla2Psi = psi.ddiff(X)
def __init__(self): self.p = observables.monomials(2)
# define domain bounds = np.array([[-2, 2], [-2, 2]]) boxes = np.array([50, 50]) Omega = domain.discretization(bounds, boxes) # define system gamma = -0.8 delta = -0.7 def b(x): return np.array([gamma * x[0, :], delta * (x[1, :] - x[0, :]**2)]) # define observables psi = observables.monomials(8) # generate data X = Omega.rand(1000) # generate test points Y = b(X) # apply generator EDMD evs = 8 # number of eigenvalues/eigenfunctions to be computed K, d, V = algorithms.gedmd(X, Y, None, psi, evs=evs, operator='K') # printMatrix(K, 'K_gEDMD') printVector(np.real(d), 'd_gEDMD') V[:, 1] /= V[3, 1] V[:, 3] /= V[10, 3] V[:, 4] /= V[6, 4] # normalize eigenvectors for convenience for i in range(evs):
#%% Traditional LQR lq = dlqr(A, B, Q, R) C = lq[0] #%% Construct snapshots of u from random agent and initial states x0 N = 10000 action_range = 10 state_range = 10 U = np.random.rand(1,N)*action_range*np.random.choice(np.array([-1,1]), size=(1,N)) X0 = np.random.rand(2,N)*state_range*np.random.choice(np.array([-1,1]), size=(2,N)) #%% Construct snapshots of states following dynamics f Y = f(X0, U) #%% Estimate Koopman tensor tensor = KoopmanTensor(X0, Y, U, phi=observables.monomials(2), psi=observables.monomials(2)) #%% Training error norms = np.empty((N)) for i in range(N): phi_x = np.vstack(tensor.Phi_X[:,i]) # current (lifted) state action = np.vstack(U[:,i]) true_x_prime = np.vstack(Y[:,i]) predicted_x_prime = tensor.B.T @ tensor.K_(action) @ phi_x # Compute norms norms[i] = utilities.l2_norm(true_x_prime, predicted_x_prime) print("Training error:", np.mean(norms))
Y = np.empty(X.shape) Z = np.empty((2, 2, X.shape[1])) for i in range(X.shape[1]): s.c = U[0, i] # X_prime[:, i] = f(X[:, 0], s.beta, s.c) # Y[:, i] = y + s.b(y)*h + s.sigma(y)*np.sqrt(h)*np.random.randn() Y[:, i] = s.b(X[:, i]) Z[0, 0, i] = s.sigma(X[:, i]) Z[0, 1, i] = 0 Z[1, 1, i] = s.sigma(X[:, i]) Z[1, 0, i] = 0 #%% Define observables order = 6 phi = observables.monomials(order) psi = observables.monomials(order) #lambda u: np.array([1]) #%% Build Phi and Psi matrices N = X.shape[1] Phi_X = phi(X) Psi_U = psi(U) #np.ones((1,N)) dim_phi = Phi_X[:, 0].shape[0] dim_psi = Psi_U[:, 0].shape[0] dPhi_Y = np.einsum('ijk,jk->ik', phi.diff(X), Y) ddPhi_X = phi.ddiff(X) # second-order derivatives S = np.einsum('ijk,ljk->ilk', Z, Z) # sigma \cdot sigma^T for i in range(dim_phi): dPhi_Y[i, :] += 0.5 * np.sum(ddPhi_X[i, :, :, :] * S, axis=(0, 1))