def make_k_chose_e(e_vec, k_vec): """ Computes the product :math:`{\mathbf{n} \choose \mathbf{k}}` :param e_vec: the vector e :type e_vec: :class:`numpy.array` :param k_vec: the vector k :type k_vec: :class:`numpy.array` :return: a scalar """ return product([sp.factorial(k) / (sp.factorial(e) * sp.factorial(k - e)) for e,k in zip(e_vec, k_vec)])
def _make_s_pow_e(self, reac_idx, e_vec): """ Compute s^e in equation 11 (see Ale et al. 2013) :param reac_idx: the index (that is the column in the stoichiometry matrix) of the reaction to consider. :type reac_idx: `int` :param e_vec: the vector e :return: a scalar (s^e) """ return product([self.__stoichoimetry_matrix[i, reac_idx] ** e for i,e in enumerate(e_vec)])
def get_one_over_n_factorial(counter_entry): r""" Calculates the :math:`\frac{1}{\mathbf{n!}}` of eq. 6 (see Ale et al. 2013). That is the invert of a product of factorials. :param counter_entry: an entry of counter. That is an array of integers of length equal to the number of variables. For instance, `counter_entry` could be `[1,0,1]` for three variables. :return: a scalar as a sympy expression """ # compute all factorials factos = [sp.factorial(c) for c in counter_entry] # multiply them prod = product(factos) # return the invert return sp.Integer(1)/sp.S(prod)
def _make_f_of_x(self, k_vec, e_vec, reaction): r""" Calculates :math:`F():math:` in eq. 12 (see Ale et al. 2013) for a specific reaction , :math:`k` and :math:`e` :param k_vec: the vector :math:`k` :param e_vec: the vector :math:`e` :param reaction: the equation of the reaction {:math:`a(x) in the model} :return: :math:`F()` """ # product of all values of {x ^ (k - e)} for all combination of e and k prod = product([var ** (k_vec[i] - e_vec[i]) for i,var in enumerate(self.__species)]) # multiply the product by the propensity {a(x)} return prod * reaction
def _make_s_pow_e(self, reac_idx, e_vec): """ Compute s^e in equation 11 (see Ale et al. 2013) :param reac_idx: the index (that is the column in the stoichiometry matrix) of the reaction to consider. :type reac_idx: `int` :param e_vec: the vector e :return: a scalar (s^e) """ return product([ self.__stoichoimetry_matrix[i, reac_idx]**e for i, e in enumerate(e_vec) ])
def _gamma_factorial(self, expr, n): r""" Compute :math:`\frac {(\alpha)_m = (\alpha + m - 1)!}{(\alpha - 1)!}` See Eq. 3 in Gamma moment closure Lakatos 2014 unpublished :param expr: a symbolic expression :type expr: :param n: :type n: `int` :return: a symbolic expression """ if n == 0: return 1 return product([expr + i for i in range(n)])
def _gamma_factorial(self, expr, n): r""" Compute :math:`\frac {(\alpha)_m = (\alpha + m - 1)!}{(\alpha - 1)!}` See Eq. 3 in Gamma moment closure Lakatos 2014 unpublished :param expr: a symbolic expression :type expr: :param n: :type n: `int` :return: a symbolic expression """ if n == 0: return 1 return product([expr+i for i in range(n)])
def _make_f_of_x(self, k_vec, e_vec, reaction): r""" Calculates :math:`F():math:` in eq. 12 (see Ale et al. 2013) for a specific reaction , :math:`k` and :math:`e` :param k_vec: the vector :math:`k` :param e_vec: the vector :math:`e` :param reaction: the equation of the reaction {:math:`a(x) in the model} :return: :math:`F()` """ # product of all values of {x ^ (k - e)} for all combination of e and k prod = product([ var**(k_vec[i] - e_vec[i]) for i, var in enumerate(self.__species) ]) # multiply the product by the propensity {a(x)} return prod * reaction
def _compute_one_moment(self, all_trajectories, mean_trajectories, moment): # the expectation of the product: #products_of_sps = [product(trajs) for trajs in all_trajectories] n_vec = moment.n_vector to_multipl = [] for i, trajs in enumerate(zip(*all_trajectories)): mean_of_sp = mean_trajectories[i] order_of_sp = n_vec[i] xi_minus_ex = [(t - mean_of_sp) ** order_of_sp for t in trajs] for x in xi_minus_ex: x.set_description(moment) to_multipl.append(xi_minus_ex) to_sum = [product(xs) for xs in zip(*to_multipl)] return sum(to_sum)/ float(len(to_sum))
def _compute_one_closed_central_moment(self, moment, covariance_matrix): r""" Compute each row of closed central moment based on Isserlis' Theorem of calculating higher order moments of multivariate normal distribution in terms of covariance matrix :param moment: moment matrix :param covariance_matrix: matrix containing variances and covariances :return: each row of closed central moment """ # If moment order is odd, higher order moments equals 0 if moment.order % 2 != 0: return sp.Integer(0) # index of species idx = [i for i in range(len(moment.n_vector))] # repeat the index of a species as many time as its value in counter list_for_partition = reduce( operator.add, map(lambda i, c: [i] * c, idx, moment.n_vector)) # If moment order is even, :math: '\mathbb{E} [x_1x_2 \ldots x_2_n] = \sum \prod\mathbb{E} [x_ix_j] ' # e.g.:math: '\mathbb{E} [x_1x_2x_3x_4] = \mathbb{E} [x_1x_2] +\mathbb{E} [x_1x_3] +\mathbb{E} [x_1x_4] # +\mathbb{E} [x_2x_3]+\mathbb{E} [x_2x_4]+\mathbb{E} [x_3x_4]' # For second order moment, there is only one way of partitioning. Hence, no need to generate partitions if moment.order == 2: return covariance_matrix[list_for_partition[0], list_for_partition[1]] # For even moment order other than 2, generate a list of partitions of the indices of covariances else: each_row = [] for idx_pair in self._generate_partitions(list_for_partition): # Retrieve the pairs of covariances using the pairs of partitioned indices l = [covariance_matrix[i, j] for i, j in idx_pair] # Calculate the product of each pair of covariances each_row.append(product(l)) # The corresponding closed central moment of that moment order is the sum of the products return sum(each_row)
def _compute_one_closed_central_moment(self, moment, covariance_matrix): r""" Compute each row of closed central moment based on Isserlis' Theorem of calculating higher order moments of multivariate normal distribution in terms of covariance matrix :param moment: moment matrix :param covariance_matrix: matrix containing variances and covariances :return: each row of closed central moment """ # If moment order is odd, higher order moments equals 0 if moment.order % 2 != 0: return sp.Integer(0) # index of species idx = [i for i in range(len(moment.n_vector))] # repeat the index of a species as many time as its value in counter list_for_partition = reduce(operator.add, map(lambda i, c: [i] * c, idx, moment.n_vector)) # If moment order is even, :math: '\mathbb{E} [x_1x_2 \ldots x_2_n] = \sum \prod\mathbb{E} [x_ix_j] ' # e.g.:math: '\mathbb{E} [x_1x_2x_3x_4] = \mathbb{E} [x_1x_2] +\mathbb{E} [x_1x_3] +\mathbb{E} [x_1x_4] # +\mathbb{E} [x_2x_3]+\mathbb{E} [x_2x_4]+\mathbb{E} [x_3x_4]' # For second order moment, there is only one way of partitioning. Hence, no need to generate partitions if moment.order == 2: return covariance_matrix[list_for_partition[0], list_for_partition[1]] # For even moment order other than 2, generate a list of partitions of the indices of covariances else: each_row = [] for idx_pair in self._generate_partitions(list_for_partition): # Retrieve the pairs of covariances using the pairs of partitioned indices l = [covariance_matrix[i, j] for i,j in idx_pair] # Calculate the product of each pair of covariances each_row.append(product(l)) # The corresponding closed central moment of that moment order is the sum of the products return sum(each_row)
def eq_central_moments(n_counter, k_counter, dmu_over_dt, species, propensities, stoichiometry_matrix, max_order): r""" Function used to calculate the terms required for use in equations giving the time dependence of central moments. The function returns the list Containing the sum of the following terms in in equation 9, for each of the :math:`[n_1, ..., n_d]` combinations in eq. 9 where ... is ... # FIXME .. math:: \mathbf{ {n \choose k} } (-1)^{ \mathbf{n-k} } [ \alpha \frac{d\beta}{dt} + \beta \frac{d\alpha}{dt} ] :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`] :param dmu_over_dt: du/dt in paper :param species: species matrix: y_0, y_1,..., y_d :param propensities: propensities matrix :param stoichiometry_matrix: stoichiometry matrix :return: central_moments matrix with `(len(n_counter)-1)` rows and one column per each :math:`[n_1, ... n_d]` combination """ central_moments = [] # Loops through required combinations of moments (n1,...,nd) # (does not include 0th order central moment as this is 1, # or 1st order central moment as this is 0 # copy dmu_mat matrix as a list of rows vectors (1/species) dmu_mat = [sp.Matrix(l).T for l in dmu_over_dt.tolist()] d_beta_over_dt_calculator = DBetaOverDtCalculator(propensities,n_counter,stoichiometry_matrix, species) for n_iter in n_counter: # skip zeroth moment if n_iter.order == 0 or n_iter.order > max_order: continue n_vec = n_iter.n_vector # Find all moments in k_counter that are lower than the current n_iter k_lower = [k for k in k_counter if n_iter >= k] taylor_exp_mat = [] for k_iter in k_lower: k_vec = k_iter.n_vector # (n k) binomial term in equation 9 n_choose_k = make_k_chose_e(k_vec, n_vec) # (-1)^(n-k) term in equation 9 minus_one_pow_n_minus_k = product([sp.Integer(-1) ** (n - m) for (n,m) in zip(n_vec, k_vec)]) # Calculate alpha, dalpha_over_dt terms in equation 9 alpha = product([s ** (n - k) for s, n, k in zip(species, n_vec, k_vec)]) # eq 10 {(n - k) mu_i^(-1)} corresponds to {(n - k)/s}. s is symbol for mean of a species # multiplies by alpha an the ith row of dmu_mat and sum it to get dalpha_over_dt # eq 10 {(n - k) mu_i^(-1)} corresponds to {(n - k)/s} dalpha_over_dt = sympy_sum_list([((n - k) / s) * alpha * mu_row for s, n, k, mu_row in zip(species, n_vec, k_vec, dmu_mat)]) # e_counter contains elements of k_counter lower than the current k_iter e_counter = [k for k in k_counter if k_iter >= k and k.order > 0] dbeta_over_dt = d_beta_over_dt_calculator.get(k_iter.n_vector, e_counter) # Calculate beta, dbeta_over_dt terms in equation 9 if len(e_counter) == 0: beta = 1 else: beta = k_iter.symbol taylor_exp_mat.append(n_choose_k * minus_one_pow_n_minus_k * (alpha * dbeta_over_dt + beta * dalpha_over_dt)) # Taylorexp is a matrix which has an entry equal to # the `n_choose_k * minus_one_pow_n_minus_k * (AdB/dt + beta dA/dt)` term in equation 9 for each k1,..,kd # These are summed over to give the Taylor Expansion for each n1,..,nd combination in equation 9 central_moments.append(sum_of_cols(sp.Matrix(taylor_exp_mat))) return sp.Matrix(central_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