def _substitute_raw_with_central(self, central_moments_exprs, central_from_raw_exprs, n_counter, k_counter): r""" Takes the expressions for central moments, and substitute the symbols representing raw moments, by equivalent expressions in terms of central moment :param central_moments_exprs: a matrix of expressions for central moments. :param central_from_raw_exprs: central moment expressed in terms of raw moments :param n_counter: a list of :class:`~means.core.descriptors.Moment`\s representing central moments :type n_counter: list[:class:`~means.core.descriptors.Moment`] :param k_counter: a list of :class:`~means.core.descriptors.Moment`\s representing raw moments :type k_counter: list[:class:`~means.core.descriptors.Moment`] :return: expressions for central moments without raw moment """ positiv_raw_moms_symbs = [ raw.symbol for raw in k_counter if raw.order > 1 ] # The symbols for the corresponding central moment central_symbols = [ central.symbol for central in n_counter if central.order > 1 ] # Now we state (central_symbols == central_from_raw_exprs) eq_to_solve = [ cfr - cs for (cs, cfr) in zip(central_symbols, central_from_raw_exprs) ] # And we solve this for the symbol of the corresponding raw moment. This gives an expression # of the symbol for raw moment in terms of central moments and lower order raw moment solved_xs = sp.Matrix([ quick_solve(eq, raw) for (eq, raw) in zip(eq_to_solve, positiv_raw_moms_symbs) ]) # now we want to express raw moments only in terms od central moments and means # for instance if we have: :math:`x_1 = 1; x_2 = 2 +x_1 and x_3 = x_2*x_1`, we should give: # :math:`x_1 = 1; x_2 = 2+1 and x_3 = 1*(2+1)` # To achieve this, we recursively apply substitution as many times as the highest order (minus one) max_order = max([p.order for p in k_counter]) for i in range(max_order - 1): substitution_pairs = zip(positiv_raw_moms_symbs, solved_xs) solved_xs = substitute_all(solved_xs, substitution_pairs) # we finally build substitution pairs to replace all raw moments substitution_pairs = zip(positiv_raw_moms_symbs, solved_xs) # apply this substitution to all elements of the central moment expressions matrix out_exprs = substitute_all(central_moments_exprs, substitution_pairs) return out_exprs
def close(self, mfk, central_from_raw_exprs, n_counter, k_counter): """ In MFK, replaces symbol for high order (order == max_order+1) by parametric expressions. That is expressions depending on lower order moments such as means, variances, covariances and so on. :param mfk: the right hand side equations containing symbols for high order central moments :param central_from_raw_exprs: expressions of central moments in terms of raw moments :param n_counter: a list of :class:`~means.core.descriptors.Moment`\s representing central moments :type n_counter: list[:class:`~means.core.descriptors.Moment`] :param k_counter: a list of :class:`~means.core.descriptors.Moment`\s representing raw moments :type k_counter: list[:class:`~means.core.descriptors.Moment`] :return: the modified MFK :rtype: `sympy.Matrix` """ # we obtain expressions for central moments in terms of variances/covariances closed_central_moments = self._compute_closed_central_moments(central_from_raw_exprs, n_counter, k_counter) # set mixed central moment to zero iff univariate closed_central_moments = self._set_mixed_moments_to_zero(closed_central_moments, n_counter) # retrieve central moments from problem moment. Typically, :math: `[yx2, yx3, ...,yxN]`. # now we want to replace the new mfk (i.e. without highest order moment) any # symbol for highest order central moment by the corresponding expression (computed above) positive_n_counter = [n for n in n_counter if n.order > 0] substitutions_pairs = [(n.symbol, ccm) for n,ccm in zip(positive_n_counter, closed_central_moments) if n.order > self.max_order] new_mfk = substitute_all(mfk, substitutions_pairs) return new_mfk
def test_substitute_all_on_expression(self): to_substitute = sympy.sympify("a*b + c*d + d*e + e*f") pairs = zip(to_sympy_matrix(["a","d","e","c","b"]), to_sympy_matrix(["z","w","v","x","y"])) expected = sympy.sympify("z*y + x*w + w*v + v*f") answer = substitute_all(to_substitute, pairs) self.assertEqual(answer, expected)
def test_substitute_all_on_expression(self): to_substitute = sympy.sympify("a*b + c*d + d*e + e*f") pairs = zip(to_sympy_matrix(["a", "d", "e", "c", "b"]), to_sympy_matrix(["z", "w", "v", "x", "y"])) expected = sympy.sympify("z*y + x*w + w*v + v*f") answer = substitute_all(to_substitute, pairs) self.assertEqual(answer, expected)
def test_substitute_all_on_matrix(self): to_substitute = to_sympy_matrix(["a*b","c*d","d*e","e*f"]) pairs = zip(to_sympy_matrix(["a","d","e","c","b"]), to_sympy_matrix(["z","w","v","x","y"])) expected = sympy.Matrix(["z*y","x*w","w*v","v*f"]) answer = substitute_all(to_substitute, pairs) self.assertEqual(answer, expected)
def test_substitute_all_on_matrix(self): to_substitute = to_sympy_matrix(["a*b", "c*d", "d*e", "e*f"]) pairs = zip(to_sympy_matrix(["a", "d", "e", "c", "b"]), to_sympy_matrix(["z", "w", "v", "x", "y"])) expected = sympy.Matrix(["z*y", "x*w", "w*v", "v*f"]) answer = substitute_all(to_substitute, pairs) self.assertEqual(answer, expected)
def _substitute_raw_with_central(self, central_moments_exprs, central_from_raw_exprs, n_counter, k_counter): r""" Takes the expressions for central moments, and substitute the symbols representing raw moments, by equivalent expressions in terms of central moment :param central_moments_exprs: a matrix of expressions for central moments. :param central_from_raw_exprs: central moment expressed in terms of raw moments :param n_counter: a list of :class:`~means.core.descriptors.Moment`\s representing central moments :type n_counter: list[:class:`~means.core.descriptors.Moment`] :param k_counter: a list of :class:`~means.core.descriptors.Moment`\s representing raw moments :type k_counter: list[:class:`~means.core.descriptors.Moment`] :return: expressions for central moments without raw moment """ positiv_raw_moms_symbs = [raw.symbol for raw in k_counter if raw.order > 1] # The symbols for the corresponding central moment central_symbols= [central.symbol for central in n_counter if central.order > 1] # Now we state (central_symbols == central_from_raw_exprs) eq_to_solve = [cfr - cs for (cs, cfr) in zip(central_symbols, central_from_raw_exprs)] # And we solve this for the symbol of the corresponding raw moment. This gives an expression # of the symbol for raw moment in terms of central moments and lower order raw moment solved_xs = sp.Matrix([quick_solve(eq,raw) for (eq, raw) in zip(eq_to_solve, positiv_raw_moms_symbs)]) # now we want to express raw moments only in terms od central moments and means # for instance if we have: :math:`x_1 = 1; x_2 = 2 +x_1 and x_3 = x_2*x_1`, we should give: # :math:`x_1 = 1; x_2 = 2+1 and x_3 = 1*(2+1)` # To achieve this, we recursively apply substitution as many times as the highest order (minus one) max_order = max([p.order for p in k_counter]) for i in range(max_order - 1): substitution_pairs = zip(positiv_raw_moms_symbs, solved_xs) solved_xs = substitute_all(solved_xs, substitution_pairs) # we finally build substitution pairs to replace all raw moments substitution_pairs = zip(positiv_raw_moms_symbs, solved_xs) # apply this substitution to all elements of the central moment expressions matrix out_exprs = substitute_all(central_moments_exprs, substitution_pairs) return out_exprs
def _compute_closed_central_moments(self, central_from_raw_exprs, n_counter, k_counter): r""" Computes parametric expressions (e.g. in terms of mean, variance, covariances) for all central moments up to max_order + 1 order. :param central_from_raw_exprs: the expression of central moments in terms of raw moments :param n_counter: a list of :class:`~means.core.descriptors.Moment`\s representing central moments :type n_counter: list[:class:`~means.core.descriptors.Moment`] :param k_counter: a list of :class:`~means.core.descriptors.Moment`\s representing raw moments :type k_counter: list[:class:`~means.core.descriptors.Moment`] :return: the central moments where raw moments have been replaced by parametric expressions :rtype: `sympy.Matrix` """ closed_raw_moments = self._compute_raw_moments(n_counter, k_counter) assert(len(central_from_raw_exprs) == len(closed_raw_moments)) # raw moment lef hand side symbol raw_symbols = [raw.symbol for raw in k_counter if raw.order > 1] # we want to replace raw moments symbols with closed raw moment expressions (in terms of variances/means) substitution_pairs = zip(raw_symbols, closed_raw_moments) # so we can obtain expression of central moments in terms of low order raw moments closed_central_moments = substitute_all(central_from_raw_exprs, substitution_pairs) return closed_central_moments
def simulate_system(self, parameters, initial_conditions, timepoints, max_moment_order=1, number_of_processes=1): """ Perform Gillespie SSA simulations and returns trajectories for of each species. Each trajectory is interpolated at the given time points. By default, the average amounts of species for all simulations is returned. :param parameters: list of the initial values for the constants in the model. Must be in the same order as in the model :param initial_conditions: List of the initial values for the equations in the problem. Must be in the same order as these equations occur. :param timepoints: A list of time points to simulate the system for :param number_of_processes: the number of parallel process to be run :param max_moment_order: the highest moment order to calculate the trajectories to. if set to zero, the individual trajectories will be returned, instead of the averaged moments. E.g. a value of one will return means, a values of two, means, variances and covariance and so on. :return: a list of :class:`~means.simulation.Trajectory` one per species in the problem, or a list of lists of trajectories (one per simulation) if `return_average == False`. :rtype: list[:class:`~means.simulation.Trajectory`] """ max_moment_order = int(max_moment_order) assert(max_moment_order >= 0) n_simulations = self.__n_simulations self._validate_parameters(parameters, initial_conditions) t_max= max(timepoints) substitution_pairs = dict(zip(self.__problem.parameters, parameters)) propensities = substitute_all(self.__problem.propensities, substitution_pairs) # lambdify the propensities for fast evaluation propensities_as_function = self.__problem.propensities_as_function def f(*species_parameters): return propensities_as_function(*(np.concatenate((species_parameters, parameters)))) population_rates_as_function = f if not self.__random_seed: seed_for_processes = [None] * n_simulations else: seed_for_processes = [i for i in range(self.__random_seed, n_simulations + self.__random_seed)] if number_of_processes ==1: ssa_generator = _SSAGenerator(population_rates_as_function, self.__problem.change, self.__problem.species, initial_conditions, t_max, seed=self.__random_seed) results = map(ssa_generator.generate_single_simulation, seed_for_processes) else: p = multiprocessing.Pool(number_of_processes, initializer=multiprocessing_pool_initialiser, initargs=[population_rates_as_function, self.__problem.change, self.__problem.species, initial_conditions, t_max, self.__random_seed]) results = p.map(multiprocessing_apply_ssa, seed_for_processes) p.close() p.join() resampled_results = [[traj.resample(timepoints, extrapolate=True) for traj in res] for res in results] for i in resampled_results: idx = len(i[0].values) - 1 if max_moment_order == 0: # Return a list of TrajectoryCollection objects return map(TrajectoryCollection, resampled_results) moments = self._compute_moments(resampled_results, max_moment_order) return TrajectoryCollection(moments)
def _get_parameter_symbols(self, n_counter, k_counter): r""" Calculates parameters Y expressions and beta coefficients in :math:`X = {A(\beta_0,\beta_1\ldots \beta_n) \cdot Y}` :param n_counter: a list of :class:`~means.core.descriptors.Moment`\s representing central moments :type n_counter: list[:class:`~means.core.descriptors.Moment`] :param k_counter: a list of :class:`~means.core.descriptors.Moment`\s representing raw moments :type k_counter: list[:class:`~means.core.descriptors.Moment`] :return: two column matrices Y expressions and beta multipliers """ n_moment = self.max_order + 1 expectation_symbols = sp.Matrix( [n.symbol for n in k_counter if n.order == 1]) n_species = len(expectation_symbols) # Create auxiliary symbolic species Y_{ij}, for i,j = 0 ... (n-1) and mirror, so that Y_{ij}=Y_{ji} symbolic_species = sp.Matrix([[ sp.Symbol(('Y_{0}'.format(str(j))) + '{0}'.format(str(i))) for i in range(n_species) ] for j in range(n_species)]) for i in range(n_species): for j in range(i + 1, n_species): symbolic_species[j, i] = symbolic_species[i, j] # Obtain beta terms explaining how original variables are derived from auxiliary ones if self.is_multivariate: # :math: `X_i = \sum_j Y_{ij}` beta_in_matrix = sp.Matrix( [sum(symbolic_species[i, :]) for i in range(n_species)]) else: # In univariate case, only the diagonal elements are needed beta_in_matrix = sp.Matrix( [symbolic_species[i, i] for i in range(n_species)]) # Covariance symbols are read into a matrix. Variances are the diagonal elements covariance_matrix = sp.Matrix( n_species, n_species, lambda x, y: self._get_covariance_symbol(n_counter, x, y)) variance_symbols = sp.Matrix( [covariance_matrix[i, i] for i in range(n_species)]) # Compute :math: `\beta_i = Var(X_i)/\mathbb{E}(X_i) \bar\alpha_i = \mathbb{E}(X_i)^2/Var(X_i)` beta_exprs = sp.Matrix( [v / e for e, v in zip(expectation_symbols, variance_symbols)]) alpha_bar_exprs = sp.Matrix([ (e**2) / v for e, v in zip(expectation_symbols, variance_symbols) ]) if self.is_multivariate: # Calculate nondiagonal elements from covariances alpha_exprs = sp.Matrix( n_species, n_species, lambda i, j: covariance_matrix[i, j] / (beta_exprs[i] * beta_exprs[j])) else: # Covariances are zero in univariate case alpha_exprs = sp.Matrix(n_species, n_species, lambda i, j: 0) for sp_idx in range(n_species): # Compute diagonal elements as :math: `\alpha_{ii} = \bar\alpha_{i} - \sum(\alpha_{ij})` //equiv to \bar\alpha_{i} in univariate alpha_exprs[sp_idx, sp_idx] = 0 alpha_exprs[sp_idx, sp_idx] = alpha_bar_exprs[sp_idx] - sum( alpha_exprs[sp_idx, :]) # Each row in moment matrix contains the exponents of Xs for a given moment # Each row in Y_exprs and beta_multipliers has elements on the appropriate power # determined by the corresponding row in the moment matrix Y_exprs = [] beta_multipliers = [] positive_n_counter = [n for n in n_counter if n.order > 0] for mom in positive_n_counter: Y_exprs.append( product([(b**s).expand() for b, s in zip(beta_in_matrix, mom.n_vector)])) beta_multipliers.append( product([(b**s).expand() for b, s in zip(beta_exprs, mom.n_vector)])) Y_exprs = sp.Matrix(Y_exprs).applyfunc(sp.expand) beta_multipliers = sp.Matrix(beta_multipliers) # Substitute alpha expressions in place of symbolic species Ys # by going through all powers up to the moment order for closure subs_pairs = [] for i, a in enumerate(alpha_exprs): Y_to_substitute = [ symbolic_species[i]**n for n in range(2, n_moment + 1) ] # Obtain alpha term for higher order moments :math: `\mathbb{E}(Y_{ij}^n) \rightarrow (\alpha_{ij})_n` alpha_m = [ self._gamma_factorial(a, n) for n in range(2, n_moment + 1) ] # Substitute alpha term for symbolic species subs_pairs += zip(Y_to_substitute, alpha_m) subs_pairs.append((symbolic_species[i], a)) # Add first order expression to the end Y_exprs = substitute_all(Y_exprs, subs_pairs) return Y_exprs, beta_multipliers
def _get_parameter_symbols(self, n_counter, k_counter): r""" Calculates parameters Y expressions and beta coefficients in :math:`X = {A(\beta_0,\beta_1\ldots \beta_n) \cdot Y}` :param n_counter: a list of :class:`~means.core.descriptors.Moment`\s representing central moments :type n_counter: list[:class:`~means.core.descriptors.Moment`] :param k_counter: a list of :class:`~means.core.descriptors.Moment`\s representing raw moments :type k_counter: list[:class:`~means.core.descriptors.Moment`] :return: two column matrices Y expressions and beta multipliers """ n_moment = self.max_order + 1 expectation_symbols = sp.Matrix([n.symbol for n in k_counter if n.order == 1]) n_species = len(expectation_symbols) # Create auxiliary symbolic species Y_{ij}, for i,j = 0 ... (n-1) and mirror, so that Y_{ij}=Y_{ji} symbolic_species=sp.Matrix([[sp.Symbol(('Y_{0}'.format(str(j)))+'{0}'.format(str(i))) for i in range(n_species)]for j in range(n_species)]) for i in range(n_species): for j in range(i+1,n_species): symbolic_species[j,i]=symbolic_species[i,j] # Obtain beta terms explaining how original variables are derived from auxiliary ones if self.is_multivariate: # :math: `X_i = \sum_j Y_{ij}` beta_in_matrix = sp.Matrix([sum(symbolic_species[i,:]) for i in range(n_species)]) else : # In univariate case, only the diagonal elements are needed beta_in_matrix = sp.Matrix([symbolic_species[i,i] for i in range(n_species)]) # Covariance symbols are read into a matrix. Variances are the diagonal elements covariance_matrix = sp.Matrix(n_species,n_species, lambda x,y: self._get_covariance_symbol(n_counter,x,y)) variance_symbols = sp.Matrix([covariance_matrix[i,i] for i in range(n_species)]) # Compute :math: `\beta_i = Var(X_i)/\mathbb{E}(X_i) \bar\alpha_i = \mathbb{E}(X_i)^2/Var(X_i)` beta_exprs = sp.Matrix([v / e for e,v in zip(expectation_symbols,variance_symbols)]) alpha_bar_exprs = sp.Matrix([(e ** 2) / v for e,v in zip(expectation_symbols,variance_symbols)]) if self.is_multivariate: # Calculate nondiagonal elements from covariances alpha_exprs = sp.Matrix(n_species,n_species, lambda i,j: covariance_matrix[i,j]/(beta_exprs[i]*beta_exprs[j])) else: # Covariances are zero in univariate case alpha_exprs = sp.Matrix(n_species,n_species, lambda i,j: 0) for sp_idx in range(n_species): # Compute diagonal elements as :math: `\alpha_{ii} = \bar\alpha_{i} - \sum(\alpha_{ij})` //equiv to \bar\alpha_{i} in univariate alpha_exprs[sp_idx,sp_idx]=0 alpha_exprs[sp_idx,sp_idx]=alpha_bar_exprs[sp_idx]-sum(alpha_exprs[sp_idx,:]) # Each row in moment matrix contains the exponents of Xs for a given moment # Each row in Y_exprs and beta_multipliers has elements on the appropriate power # determined by the corresponding row in the moment matrix Y_exprs = [] beta_multipliers = [] positive_n_counter = [n for n in n_counter if n.order > 0] for mom in positive_n_counter: Y_exprs.append(product([(b ** s).expand() for b, s in zip(beta_in_matrix, mom.n_vector)])) beta_multipliers.append(product([(b ** s).expand() for b, s in zip(beta_exprs, mom.n_vector)])) Y_exprs = sp.Matrix(Y_exprs).applyfunc(sp.expand) beta_multipliers = sp.Matrix(beta_multipliers) # Substitute alpha expressions in place of symbolic species Ys # by going through all powers up to the moment order for closure subs_pairs = [] for i,a in enumerate(alpha_exprs): Y_to_substitute = [symbolic_species[i]**n for n in range(2, n_moment+1)] # Obtain alpha term for higher order moments :math: `\mathbb{E}(Y_{ij}^n) \rightarrow (\alpha_{ij})_n` alpha_m = [self._gamma_factorial(a,n) for n in range(2, n_moment+1)] # Substitute alpha term for symbolic species subs_pairs += zip(Y_to_substitute, alpha_m) subs_pairs.append((symbolic_species[i], a)) # Add first order expression to the end Y_exprs = substitute_all(Y_exprs, subs_pairs) return Y_exprs, beta_multipliers