def test_ode_rhs_as_function_cache_does_not_persist_between_instances(self): """ Given two ODEProblems, the cache should not persist between these objects. :return: """ p1_lhs = [Moment(np.ones(4), i) for i in sympy.Matrix(['y_1', 'y_2', 'y_3', 'y_4'])] p1_rhs = to_sympy_matrix(['y_1+y_2+c_2', 'y_2+y_3+c_3', 'y_3+c_1', 'y_1*2']) p2_lhs = [Moment(np.ones(3), i) for i in sympy.Matrix(['y_1', 'y_2', 'y_3'])] p2_rhs = to_sympy_matrix(['y_1', 'c_1', 'y_2+y_3']) p1 = ODEProblem('MEA', p1_lhs, p1_rhs, parameters=sympy.symbols(['c_1', 'c_2', 'c_3'])) p1_rhs_as_function = p1.right_hand_side_as_function p2 = ODEProblem('MEA', p2_lhs, p2_rhs, parameters=sympy.symbols(['c_1', 'c_2', 'c_3'])) p2_rhs_as_function = p2.right_hand_side_as_function constants = [1, 2, 3] values_p1 = [4, 5, 6, 5] # y_1, y_2, y_3, y_4 in that order values_p2 = [4, 5, 6] # y_1, y_2, y_3 in that order p1_expected_ans = np.array([11, 14, 7, 8]) p2_expected_ans = np.array([4, 1, 6+5]) p1_actual_ans = np.array(p1_rhs_as_function(values_p1, constants)) p2_actual_ans = np.array(p2_rhs_as_function(values_p2, constants)) # This checks if by any means p2 is able to "override" the p1 result p1_ans_after_p2 = np.array(p1_rhs_as_function(values_p1, constants)) assert_array_equal(p1_actual_ans, p1_expected_ans) assert_array_equal(p2_actual_ans, p2_expected_ans) assert_array_equal(p1_ans_after_p2, p1_expected_ans)
def _generate_ode_problem(): # Create an ODEProblem from dimer instance c_0 = Symbol('c_0') c_1 = Symbol('c_1') c_2 = Symbol('c_2') constants = [c_0, c_1, c_2] y_0 = Symbol('y_0') yx1 = Symbol('yx1') right_hand_side = MutableDenseMatrix( [[ -2 * c_0 * y_0 * (y_0 - 1) - 2 * c_0 * yx1 + 2 * c_1 * (Float('0.5', prec=15) * c_2 - Float('0.5', prec=15) * y_0) ], [ Float('4.0', prec=15) * c_0 * y_0**2 - Float('4.0', prec=15) * c_0 * y_0 + Float('2.0', prec=15) * c_1 * c_2 - Float('2.0', prec=15) * c_1 * y_0 - yx1 * (Float('8.0', prec=15) * c_0 * y_0 - Float('8.0', prec=15) * c_0 + Float('2.0', prec=15) * c_1) ]]) ode_lhs_terms = [ Moment(np.array([1]), symbol=y_0), Moment(np.array([2]), symbol=yx1) ] dimer_problem = ODEProblem('MEA', ode_lhs_terms, right_hand_side, constants) return dimer_problem
def _get_rhs_as_function(args): lhs, rhs = args p = ODEProblem('MEA', lhs, rhs, parameters=sympy.symbols(['c_1', 'c_2', 'c_3'])) # access right_hand_side_as_function rhs_as_function = p.right_hand_side_as_function return p
def test_ode_moment_no_description_from_variance_terms(self): """ Given Variance terms as left hand side terms, the generated descriptions dict should have nones for each of the symbols """ lhs = [VarianceTerm(pos, term) for term, pos in [('V34', (3, 4)), ('V32', (3, 2)), ('V11', (1, 1))]] rhs = to_sympy_matrix(['y_1+y_2+c_2', 'y_2+y_3+c_3', 'y_3+c_1']) p = ODEProblem('LNA', lhs, rhs, parameters=sympy.symbols(['c_1', 'c_2', 'c_3'])) for i,l in enumerate(lhs): self.assertIsNone(p._descriptions_dict[l.symbol].descriptor)
def test_ode_moment_getting_n_vector_from_dict_and_key(self): """ Given a list of descriptor and a list of symbols used to create Moment, Then problem descriptor_for_symbol function should return the correct descriptor for each corresponding symbol :return: """ symbs = to_sympy_matrix(['y_1', 'y_2', 'y_3']) desc = [[0, 0, 1], [1, 0, 432], [21, 43, 34]] lhs = [Moment(d, s) for d, s in zip(desc, symbs)] rhs = to_sympy_matrix(['y_1+y_2+c_2', 'y_2+y_3+c_3', 'y_3+c_1']) p = ODEProblem('MEA', lhs, rhs, parameters=sympy.symbols(['c_1', 'c_2', 'c_3'])) for i, l in enumerate(lhs): self.assertEqual(p.descriptor_for_symbol(l.symbol), l)
def test_ode_rhs_as_function(self): """ Given an ODEProblem with well specified LHS, RHS expressions as well as list of constants, the value of rhs_as_function given the appropriate params should be the same as the value of rhs evaluated for these params. The returned answer should also be an one-dimensional numpy array. :return: """ lhs = [Moment(np.ones(3),i) for i in sympy.Matrix(['y_1', 'y_2', 'y_3'])] rhs = to_sympy_matrix(['y_1+y_2+c_2', 'y_2+y_3+c_3', 'y_3+c_1']) p = ODEProblem('MEA', lhs, rhs, parameters=sympy.symbols(['c_1', 'c_2', 'c_3'])) rhs_as_function = p.right_hand_side_as_function values = [4, 5, 6] # y_1, y_2, y_3 in that order expected_ans = np.array([11, 14, 7]) actual_ans = np.array(rhs_as_function(values, [1, 2, 3])) self.assertEqual(actual_ans.ndim, 1) # Returned answer must be an one-dimensional array, # otherwise ExplicitEuler solver would fail. assert_array_equal(actual_ans, expected_ans)
def run(self): r""" Overrides the default run() method. Performs the complete analysis on the model specified during initialisation. :return: an ODE problem which can be further used in inference and simulation. :rtype: :class:`~means.core.problems.ODEProblem` """ max_order = self.__max_order stoichiometry_matrix = self.model.stoichiometry_matrix propensities = self.model.propensities species = self.model.species # compute n_counter and k_counter; the "n" and "k" vectors in equations, respectively. n_counter, k_counter = generate_n_and_k_counters(max_order, species) # dmu_over_dt has row per species and one col per element of n_counter (eq. 6) dmu_over_dt = generate_dmu_over_dt(species, propensities, n_counter, stoichiometry_matrix) # Calculate expressions to use in central moments equations (eq. 9) central_moments_exprs = eq_central_moments(n_counter, k_counter, dmu_over_dt, species, propensities, stoichiometry_matrix, max_order) # Expresses central moments in terms of raw moments (and central moments) (eq. 8) central_from_raw_exprs = raw_to_central(n_counter, species, k_counter) # Substitute raw moment, in central_moments, with expressions depending only on central moments central_moments_exprs = self._substitute_raw_with_central( central_moments_exprs, central_from_raw_exprs, n_counter, k_counter) # Get final right hand side expressions for each moment in a vector mfk = self._generate_mass_fluctuation_kinetics(central_moments_exprs, dmu_over_dt, n_counter) # Applies moment expansion closure, that is replaces last order central moments by parametric expressions mfk = self.closure.close(mfk, central_from_raw_exprs, n_counter, k_counter) # These are the left hand sign symbols referring to the mfk prob_lhs = self._generate_problem_left_hand_side(n_counter, k_counter) # Finally, we build the problem out_problem = ODEProblem("MEA", prob_lhs, mfk, sp.Matrix(self.model.parameters)) return out_problem
def run(self): """ Overrides the default _run() private method. Performs the complete analysis :return: A fully computed set of Ordinary Differential Equations that can be used for further simulation :rtype: :class:`~means.core.problems.ODEProblem` """ S = self.model.stoichiometry_matrix amat = self.model.propensities ymat = self.model.species n_species = len(ymat) # dPdt is matrix of each species differentiated w.r.t. time # The code below literally multiplies the stoichiometry matrix to a column vector of propensities # from the right (::math::`\frac{dP}{dt} = \mathbf{Sa}`) dPdt = S * amat # A Is a matrix of each species (rows) and the derivatives of their stoichiometry matrix rows # against each other species # Code below computes the matrix A, that is of size `len(ymat) x len(ymat)`, for which each entry # ::math::`A_{ik} = \sum_j S_{ij} \frac{\partial a_j}{\partial y_k} = \mathfb{S_i} \frac{\partial \mathbf{a}}{\partial y_k}` A = sp.Matrix(len(ymat), len(ymat), lambda i, j: 0) for i in range(A.rows): for k in range(A.cols): A[i, k] = reduce(operator.add, [ S[i, j] * sp.diff(amat[j], ymat[k]) for j in range(len(amat)) ]) # `diagA` is a matrix that has values sqrt(a[i]) on the diagonal (0 elsewhere) diagA = sp.Matrix( len(amat), len(amat), lambda i, j: amat[i]**sp.Rational(1, 2) if i == j else 0) # E is stoichiometry matrix times diagA E = S * diagA variance_terms = [] cov_matrix = [] for i in range(len(ymat)): row = [] for j in range(len(ymat)): if i <= j: symbol = 'V_{0}_{1}'.format(i, j) variance_terms.append( VarianceTerm(position=(i, j), symbol=symbol)) else: # Since Vi,j = Vj,i, i.e. covariance are equal, we only record Vi,j but not Vj,i symbol = 'V_{0}_{1}'.format(j, i) variance_terms.append( VarianceTerm(position=(j, i), symbol=symbol)) row.append(symbol) cov_matrix.append(row) V = sp.Matrix(cov_matrix) # Matrix of variances (diagonal) and covariances of species i and j differentiated wrt time. # I.e. if i=j, V_ij is the variance, and if i!=j, V_ij is the covariance between species i and species j dVdt = A * V + V * (A.T) + E * (E.T) # build ODEProblem object rhs_redundant = sp.Matrix([i for i in dPdt] + [i for i in dVdt]) #generate ODE terms n_vectors = [ tuple([1 if i == j else 0 for i in range(n_species)]) for j in range(n_species) ] moment_terms = [ Moment(nvec, lhs) for (lhs, nvec) in zip(ymat, n_vectors) ] ode_description = moment_terms + variance_terms non_redundant_idx = [] ode_terms = [] # remove repetitive covariances, as Vij = Vji for i, cov in enumerate(ode_description): if cov in ode_terms: continue else: ode_terms.append(cov) non_redundant_idx.append(i) rhs = [] for i in non_redundant_idx: rhs.append(rhs_redundant[i]) out_problem = ODEProblem("LNA", ode_terms, rhs, sp.Matrix(self.model.parameters)) return out_problem
def _sample_problem(): lhs_terms = [ Moment(np.array([1, 0, 0]), symbol='y_0'), Moment(np.array([0, 1, 0]), symbol='y_1'), Moment(np.array([0, 0, 1]), symbol='y_2'), Moment(np.array([0, 0, 2]), symbol='yx1'), Moment(np.array([0, 1, 1]), symbol='yx2'), Moment(np.array([0, 2, 0]), symbol='yx3'), Moment(np.array([1, 0, 1]), symbol='yx4'), Moment(np.array([1, 1, 0]), symbol='yx5'), Moment(np.array([2, 0, 0]), symbol='yx6') ] constants = ['c_0', 'c_1', 'c_2', 'c_3', 'c_4', 'c_5', 'c_6'] c_0 = Symbol('c_0') c_1 = Symbol('c_1') y_0 = Symbol('y_0') c_2 = Symbol('c_2') y_2 = Symbol('y_2') c_6 = Symbol('c_6') yx4 = Symbol('yx4') yx6 = Symbol('yx6') c_3 = Symbol('c_3') c_4 = Symbol('c_4') y_1 = Symbol('y_1') c_5 = Symbol('c_5') yx2 = Symbol('yx2') yx1 = Symbol('yx1') yx3 = Symbol('yx3') yx5 = Symbol('yx5') rhs = MutableDenseMatrix([ [ c_0 - c_1 * y_0 - c_2 * y_0 * y_2 / (c_6 + y_0) + yx4 * (c_2 * y_0 / (c_6 + y_0)**2 - c_2 / (c_6 + y_0)) + yx6 * (-c_2 * y_0 * y_2 / (c_6 + y_0)**3 + c_2 * y_2 / (c_6 + y_0)**2) ], [c_3 * y_0 - c_4 * y_1], [c_4 * y_1 - c_5 * y_2], [ 2 * c_4 * y_1 * y_2 + c_4 * y_1 + 2 * c_4 * yx2 - 2 * c_5 * y_2**2 + c_5 * y_2 - 2 * c_5 * yx1 - 2 * y_2 * (c_4 * y_1 - c_5 * y_2) ], [ c_3 * y_0 * y_2 + c_3 * yx4 + c_4 * y_1**2 - c_4 * y_1 * y_2 - c_4 * y_1 + c_4 * yx3 - c_5 * y_1 * y_2 - y_1 * (c_4 * y_1 - c_5 * y_2) - y_2 * (c_3 * y_0 - c_4 * y_1) + yx2 * (-c_4 - c_5) ], [ 2 * c_3 * y_0 * y_1 + c_3 * y_0 + 2 * c_3 * yx5 - 2 * c_4 * y_1**2 + c_4 * y_1 - 2 * c_4 * yx3 - 2 * y_1 * (c_3 * y_0 - c_4 * y_1) ], [ c_0 * y_2 - c_1 * y_0 * y_2 - c_2 * y_0 * y_2**2 / (c_6 + y_0) - c_2 * y_0 * yx1 / (c_6 + y_0) + c_4 * y_0 * y_1 + c_4 * yx5 - c_5 * y_0 * y_2 - y_0 * (c_4 * y_1 - c_5 * y_2) - y_2 * (c_0 - c_1 * y_0 - c_2 * y_0 * y_2 / (c_6 + y_0)) + yx4 * (-c_1 + 2 * c_2 * y_0 * y_2 / (c_6 + y_0)**2 - 2 * c_2 * y_2 / (c_6 + y_0) - c_5 - y_2 * (c_2 * y_0 / (c_6 + y_0)**2 - c_2 / (c_6 + y_0))) + yx6 * (-c_2 * y_0 * y_2**2 / (c_6 + y_0)**3 + c_2 * y_2**2 / (c_6 + y_0)**2 - y_2 * (-c_2 * y_0 * y_2 / (c_6 + y_0)**3 + c_2 * y_2 / (c_6 + y_0)**2)) ], [ c_0 * y_1 - c_1 * y_0 * y_1 - c_2 * y_0 * y_1 * y_2 / (c_6 + y_0) - c_2 * y_0 * yx2 / (c_6 + y_0) + c_3 * y_0**2 - c_4 * y_0 * y_1 - y_0 * (c_3 * y_0 - c_4 * y_1) - y_1 * (c_0 - c_1 * y_0 - c_2 * y_0 * y_2 / (c_6 + y_0)) + yx4 * (c_2 * y_0 * y_1 / (c_6 + y_0)**2 - c_2 * y_1 / (c_6 + y_0) - y_1 * (c_2 * y_0 / (c_6 + y_0)**2 - c_2 / (c_6 + y_0))) + yx5 * (-c_1 + c_2 * y_0 * y_2 / (c_6 + y_0)**2 - c_2 * y_2 / (c_6 + y_0) - c_4) + yx6 * (-c_2 * y_0 * y_1 * y_2 / (c_6 + y_0)**3 + c_2 * y_1 * y_2 / (c_6 + y_0)**2 + c_3 - y_1 * (-c_2 * y_0 * y_2 / (c_6 + y_0)**3 + c_2 * y_2 / (c_6 + y_0)**2)) ], [ 2 * c_0 * y_0 + c_0 - 2 * c_1 * y_0**2 + c_1 * y_0 - 2 * c_2 * y_0**2 * y_2 / (c_6 + y_0) + c_2 * y_0 * y_2 / (c_6 + y_0) - 2 * y_0 * (c_0 - c_1 * y_0 - c_2 * y_0 * y_2 / (c_6 + y_0)) + yx4 * (2 * c_2 * y_0**2 / (c_6 + y_0)**2 - 4 * c_2 * y_0 / (c_6 + y_0) - c_2 * y_0 / (c_6 + y_0)**2 + c_2 / (c_6 + y_0) - 2 * y_0 * (c_2 * y_0 / (c_6 + y_0)**2 - c_2 / (c_6 + y_0))) + yx6 * (-2 * c_1 - 2 * c_2 * y_0**2 * y_2 / (c_6 + y_0)**3 + 4 * c_2 * y_0 * y_2 / (c_6 + y_0)**2 + c_2 * y_0 * y_2 / (c_6 + y_0)**3 - 2 * c_2 * y_2 / (c_6 + y_0) - c_2 * y_2 / (c_6 + y_0)**2 - 2 * y_0 * (-c_2 * y_0 * y_2 / (c_6 + y_0)**3 + c_2 * y_2 / (c_6 + y_0)**2)) ] ]) problem = ODEProblem(method='MEA', left_hand_side_descriptors=lhs_terms, right_hand_side=rhs, parameters=constants) return problem
def test_ode_problem_lna_serialisation_works(self): c_0 = Symbol('c_0') c_1 = Symbol('c_1') y_0 = Symbol('y_0') c_2 = Symbol('c_2') y_2 = Symbol('y_2') c_6 = Symbol('c_6') c_3 = Symbol('c_3') c_4 = Symbol('c_4') y_1 = Symbol('y_1') c_5 = Symbol('c_5') V_00 = Symbol('V_00') V_02 = Symbol('V_02') V_20 = Symbol('V_20') V_01 = Symbol('V_01') V_21 = Symbol('V_21') V_22 = Symbol('V_22') V_10 = Symbol('V_10') V_12 = Symbol('V_12') V_11 = Symbol('V_11') right_hand_side = MutableDenseMatrix( [[c_0 - c_1 * y_0 - c_2 * y_0 * y_2 / (c_6 + y_0)], [c_3 * y_0 - c_4 * y_1], [c_4 * y_1 - c_5 * y_2], [ 2 * V_00 * (-c_1 + c_2 * y_0 * y_2 / (c_6 + y_0)**2 - c_2 * y_2 / (c_6 + y_0)) - V_02 * c_2 * y_0 / (c_6 + y_0) - V_20 * c_2 * y_0 / (c_6 + y_0) + c_0**Float('1.0', prec=15) + (c_1 * y_0)**Float('1.0', prec=15) + (c_2 * y_0 * y_2 / (c_6 + y_0))**Float('1.0', prec=15) ], [ V_00 * c_3 - V_01 * c_4 + V_01 * (-c_1 + c_2 * y_0 * y_2 / (c_6 + y_0)**2 - c_2 * y_2 / (c_6 + y_0)) - V_21 * c_2 * y_0 / (c_6 + y_0) ], [ V_01 * c_4 - V_02 * c_5 + V_02 * (-c_1 + c_2 * y_0 * y_2 / (c_6 + y_0)**2 - c_2 * y_2 / (c_6 + y_0)) - V_22 * c_2 * y_0 / (c_6 + y_0) ], [ V_00 * c_3 - V_10 * c_4 + V_10 * (-c_1 + c_2 * y_0 * y_2 / (c_6 + y_0)**2 - c_2 * y_2 / (c_6 + y_0)) - V_12 * c_2 * y_0 / (c_6 + y_0) ], [ V_01 * c_3 + V_10 * c_3 - 2 * V_11 * c_4 + (c_3 * y_0)**Float('1.0', prec=15) + (c_4 * y_1)**Float('1.0', prec=15) ], [ V_02 * c_3 + V_11 * c_4 - V_12 * c_4 - V_12 * c_5 - (c_4 * y_1)**Float('1.0', prec=15) ], [ V_10 * c_4 - V_20 * c_5 + V_20 * (-c_1 + c_2 * y_0 * y_2 / (c_6 + y_0)**2 - c_2 * y_2 / (c_6 + y_0)) - V_22 * c_2 * y_0 / (c_6 + y_0) ], [ V_11 * c_4 + V_20 * c_3 - V_21 * c_4 - V_21 * c_5 - (c_4 * y_1)**Float('1.0', prec=15) ], [ V_12 * c_4 + V_21 * c_4 - 2 * V_22 * c_5 + (c_4 * y_1)**Float('1.0', prec=15) + (c_5 * y_2)**Float('1.0', prec=15) ]]) ode_lhs_terms = [ Moment(np.array([1, 0, 0]), symbol=y_0), Moment(np.array([0, 1, 0]), symbol=y_1), Moment(np.array([0, 0, 1]), symbol=y_2), VarianceTerm((0, 0), V_00), VarianceTerm((0, 1), V_01), VarianceTerm((0, 2), V_02), VarianceTerm((1, 0), V_10), VarianceTerm((1, 1), V_11), VarianceTerm((1, 2), V_12), VarianceTerm((2, 0), V_20), VarianceTerm((2, 1), V_21), VarianceTerm((2, 2), V_22) ] constants = ['c_0', 'c_1', 'c_2', 'c_3', 'c_4', 'c_5', 'c_6'] problem = ODEProblem('LNA', ode_lhs_terms, right_hand_side, constants) self._roundtrip(problem) # Now make sure to access problem.right_hand_side_as_function as this sometimes breaks pickle f = problem.right_hand_side_as_function # Do roundtrip again self._roundtrip(problem)