def compute_SSM(self, normalize=False, **kwargs): """ Returns the sensitivity coefficients S_j for each parameter p_j. The sensitivity coefficients are written in a sensitivity matrix SSM of size len(timepoints) x len(params) x n If normalize argument is true, the coefficients are normalized by the nominal value of each paramneter. Use mode = 'accurate' for this object attribute to use accurate computations using numdifftools. """ if 'mode' in kwargs: if kwargs.get('mode') == 'accurate_SSM': return self.solve_extended_ode(**kwargs) def sens_func(t, x, J, Z): # forms ODE to solve for sensitivity coefficient S dsdt = J @ x + Z return dsdt P = self.params_values S0 = np.zeros(self.n) # Initial value for S_i SSM = np.zeros((len(self.timepoints), len(P), self.n)) # solve for all x's in timeframe set by timepoints system_obj = self.get_system() sol = utils.get_ODE(system_obj, self.timepoints).solve_system().T xs = sol xs = np.reshape(xs, (len(self.timepoints), self.n)) self.xs = xs # Solve for SSM at each time point for k in range(len(self.timepoints)): # print('for timepoint',self.timepoints[k]) timepoints = self.timepoints[0:k + 1] if len(timepoints) == 1: continue # get the jacobian matrix J = self.compute_J(xs[k, :], **kwargs) #Solve for S = dx/dp for all x and all P (or theta, the parameters) at time point k for j in range(len(P)): utils.printProgressBar(int(j + k * len(P)), len(self.timepoints) * len(P) - 1, prefix='SSM Progress:', suffix='Complete', length=50) # print('for parameter',P[j]) # get the pmatrix Zj = self.compute_Zj(xs[k, :], j, **kwargs) # solve for S sens_func_ode = lambda t, x: sens_func(t, x, J, Zj) sol = odeint(sens_func_ode, S0, timepoints, tfirst=True) S = sol S = np.reshape(S, (len(timepoints), self.n)) SSM[k, j, :] = S[k, :] self.SSM = SSM if normalize: SSM = self.normalize_SSM( ) #Identifiablity was estimated using an normalized SSM return SSM
def get_robustness_metric(self, reduced_sys): # Create an option so the default way this is # done is given two systems compute robustness metric. # Implementing Theorem 2 timepoints_ssm = self.timepoints_ssm _, x_sols, full_ssm = self.get_solutions() S = full_ssm.compute_SSM() self.S = S reduced_ssm = utils.get_SSM(reduced_sys, timepoints_ssm) x_sols_hat = utils.get_ODE(reduced_sys, timepoints_ssm).solve_system().T x_sols = np.reshape(x_sols, (len(timepoints_ssm), self.n)) x_sols_hat = np.reshape(x_sols_hat, (len(timepoints_ssm), reduced_sys.n)) Se = np.zeros(len(self.params_values)) S_hat = reduced_ssm.compute_SSM() reduced_sys.S = S_hat S_bar = np.concatenate((S, S_hat), axis=2) S_bar = np.reshape(S_bar, (len(timepoints_ssm), self.n + reduced_sys.n, len(self.params_values))) for j in range(len(self.params_values)): S_metric_max = 0 for k in range(len(self.timepoints_ssm)): J = full_ssm.compute_J(x_sols[k, :]) J_hat = reduced_ssm.compute_J(x_sols_hat[k, :]) J_bar = block_diag(J, J_hat) # print(J) # print(J_bar) C_bar = np.concatenate((self.C, -1 * reduced_sys.C), axis=1) C_bar = np.reshape(C_bar, (np.shape(self.C)[0], (self.n + reduced_sys.n))) # if np.isnan(J).any() or np.isnan(J_hat).any() # or np.isfinite(J).all() or np.isfinite(J_hat).all(): # warnings.warn('NaN or inf found in Jacobians, continuing') # continue P = solve_lyapunov(J_bar, -1 * C_bar.T @ C_bar) if k == 0: max_eig_P = max(eigvals(P)) Z = full_ssm.compute_Zj(x_sols[k, :], j) Z_hat = reduced_ssm.compute_Zj(x_sols_hat[k, :], j) Z_bar = np.concatenate((Z, Z_hat), axis=0) Z_bar = np.reshape(Z_bar, ((self.n + reduced_sys.n), 1)) S_metric = norm(Z_bar.T @ P @ S_bar[k, :, j]) if S_metric > S_metric_max: S_metric_max = S_metric Se[j] = max_eig_P + 2 * len(reduced_ssm.timepoints) * S_metric_max utils.printProgressBar( int(j + k * len(self.params_values)), len(timepoints_ssm) * len(self.params_values) - 1, prefix='Robustness Metric Progress:', suffix='Complete', length=50) reduced_sys.Se = Se return Se
def test_ode_objects(self): import numpy as np timepoints = np.linspace(0, 10, 100) ode_object = ODE(self.x, self.f, self.params, self.C, self.g, self.h, self.u, params_values=[2, 4, 6], x_init=np.ones(4), input_values=self.input_values, timepoints=timepoints) ode_object_same = get_ODE(self.system, timepoints=timepoints) self.assertIsInstance(ode_object, ODE) self.assertIsInstance(ode_object, System) self.assertEqual(ode_object.f, ode_object_same.f) self.assertIsInstance(ode_object.get_system(), System) test_sys = TestSystem() test_sys.test_system_equality(ode_object.get_system(), self.system) test_sys.test_system_equality(ode_object.get_system(), self.system)
def get_error_metric(self, reduced_sys): # Give option for get_error_metric(sys1, sys2) """ Returns the error defined as the 2-norm of y - y_hat. y = Cx and y_hat = C_hat x_hat OR y = h(x, P), y_hat = h_hat(x_hat, P) """ reduced_ode = utils.get_ODE(reduced_sys, self.timepoints_ode) x_sol, _, _ = self.get_solutions() y = self.C @ x_sol x_sols_hat = reduced_ode.solve_system().T reduced_sys.x_sol = x_sols_hat y_hat = np.array(reduced_sys.C) @ np.array(x_sols_hat) if np.shape(y) == np.shape(y_hat): e = np.linalg.norm(y - y_hat) else: raise ValueError( 'The output dimensions must be the same for' + 'reduced and full model. Choose C and C_hat accordingly') if np.isnan(e): print('The error is NaN, something wrong...continuing.') return e