def create(model, sink, p_0=None, t_0=None, sink_0=None, time_dependencies=None, domain_states=None): """ Returns a solver for the Chemical Master Equation of the given model. arguments: model : the CME model to solve sink : If sink is True, the solver will include a 'sink' state used to accumulate any probability that may flow outside the domain. This can be used to measure the error in the solution due to truncation of the domain. If sink is False, the solver will not include a 'sink' state, and probability will be artificially prevented from flowing outside of the domain. p_0 : (optional) mapping from states in the domain to probabilities, for the initial probability distribution. If not specified, and the initial state of the state space is given by the model, defaults to all probability concentrated at the initial state, otherwise, a ValueError will be raised. t_0 : (optional) initial time, defaults to 0.0 sink_0 : (optional) initial sink probability, defaults to 0.0 Only a valid argument if sink is set to True. time_dependencies : (optional) By default, reaction propensities are time independent. If specified, time_dependencies must be of the form { s_1 : phi_1, ..., s_n : phi_n }, where each (s_j, phi_j) item satisifes : s_j : set of reaction indices phi_j : phi_j(t) -> time dependent coefficient The propensities of the reactions with indicies contained in s_j will all be multiplied by the coefficient phi_j(t), at time t. Reactions are indexed according to the ordering of the propensities in the model. The reaction index sets s_j must be *disjoint*. It is not necessary for the union of the s_j to include all the reaction indices. If a reaction's index is not contained in any s_j then the reaction is treated as time-independent. mapping of time dependent coefficient functions keyed by subsets of reaction indices, with respect to the ordering of reactions determined by the order of the propensity functions inside the model. The propensities of the reactions with indices included in each subset are multiplied by the time dependent coefficient function. By default, no time dependent coefficient functions are specified, that is, the CME has time-independent propensities. domain_states : (optional) array of states in the domain. By default, generate the rectangular lattice of states defined by the 'shape' entry of the model. A ValueError is raised if both domain_states and 'shape' are unspecified. """ mdl.validate_model(model) if sink_0 is not None: if not sink: raise ValueError("sink_0 may not be specified if sink is False") sink_0 = float(sink_0) else: sink_0 = 0.0 # determine states in domain, then construct an enumeration of the # domain states if domain_states is None: if mdl.SHAPE not in model: lament = "if no states given, model must contain key '%s'" raise KeyError(lament % mdl.SHAPE) else: domain_states = domain.from_rect(shape=model.shape) domain_enum = state_enum.create(domain_states) # determine p_0, then construct a dense representation with respect to # the domain enumeration initial_state = model.get(mdl.INITIAL_STATE, None) if p_0 is None: if initial_state is None: lament = "if no p_0 given, model must contain key '%s'" raise ValueError(lament % mdl.INITIAL_STATE) else: p_0 = {initial_state: 1.0} if t_0 is None: t_0 = 0.0 member_flags = domain_enum.contains(domain.from_iter(p_0)) if not numpy.logical_and.reduce(member_flags): raise ValueError("support of p_0 is not a subset of domain_states") # compute reaction matrices and use them to define differential equations gen_matrices = cme_matrix.gen_reaction_matrices(model, domain_enum, sink, cme_matrix.non_neg_states) reaction_matrices = list(gen_matrices) dy_dt = cme_matrix.create_diff_eqs(reaction_matrices, phi=time_dependencies) # construct and initialise solver if sink: cme_solver = ode_solver.Solver(dy_dt, y_0=(p_0, sink_0), t_0=t_0) pack, unpack = create_packing_functions(domain_enum) cme_solver.set_packing(pack, unpack, transform_dy_dt=False) else: pack = domain_enum.pack_distribution unpack = domain_enum.unpack_distribution cme_solver = ode_solver.Solver(dy_dt, y_0=p_0, t_0=t_0) cme_solver.set_packing(pack, unpack, transform_dy_dt=False) return cme_solver
def gen_reaction_matrices(model, domain_enum, sink, validity_test): """ Returns generator yielding the sparse matrices for each reaction term. Generator yielding the sparse matrices for the dp/dt term of each reaction, matching the ordering implied by the ordering of the reaction propensity functions and transtions in the model. Arguments: * ``domain_enum`` : :class:`StateEnum` instance enumerating the states in the domain * ``sink`` : boolean flag indicating if the reaction matrices should add a 'sink' state used to accumulate probability that flows outside of the domain. If sink is set to ``True``, the index of the sink state is chosen to be ``domain_enum.size`` * ``validity_test`` : a function of the form validity_test(state_array) -> bool_array Returns a boolean array of flags corresponding to those states in ``state_array`` that are valid. See: non_neg_states(state_array) """ mdl.validate_model(model) if domain_enum.offset != 0: raise NotImplementedError('non-zero domain_enum offset unsupported') sink = bool(sink) if sink: sink_index = domain_enum.size propensities = model.propensities transitions = model.transitions reactions = itertools.izip(propensities, transitions) src_states = numpy.array(domain_enum.ordered_states) src_indices = domain_enum.indices(src_states) for (propensity, transition) in reactions: # compute destination states for this transition transition = numpy.asarray(transition)[:, numpy.newaxis] dst_states = src_states + transition # determine which states have destination states inside the # truncated domain. these will be defined as the 'interior' states. # conversely, 'exterior' states are those states of the truncated # domain with destination states not in the domain. interior = domain_enum.contains(dst_states) exterior = numpy.logical_not(interior) num_int_states = numpy.add.reduce(interior) num_ext_states = numpy.add.reduce(exterior) # these lists will be used to accumulate 'COO'-ordinate format # sparse matrix data for this reaction. data = [] rows = [] cols = [] # account for the sparse matrix data for the flux out of the # interior states of the truncated domain if num_int_states > 0: int_src_states = numpy.array(src_states[:, interior]) int_src_indices = numpy.array(src_indices[interior]) int_dst_states = numpy.array(dst_states[:, interior]) int_dst_indices = domain_enum.indices(int_dst_states) int_coefficients = compute_propensity(propensity, int_src_states) # flux out data.append(-int_coefficients) cols.append(int_src_indices) rows.append(int_src_indices) # flux in data.append(int_coefficients) cols.append(int_src_indices) rows.append(int_dst_indices) # account for the sparse matrix data for the flux out of the interior # states of the truncated domain and into the sink state if sink and (num_ext_states > 0): valid = validity_test(dst_states[:, exterior]) num_valid_states = numpy.add.reduce(valid) if num_valid_states > 0: ext_src_indices = numpy.array(src_indices[exterior][valid]) ext_src_states = numpy.array(src_states[:, exterior][:, valid]) ext_coefficients = compute_propensity(propensity, ext_src_states) shape = numpy.shape(ext_src_indices) ext_dst_indices = sink_index * numpy.ones(shape, dtype = numpy.int) # these terms account for the flux out of the truncated # domain into the sink state data.append(-ext_coefficients) cols.append(ext_src_indices) rows.append(ext_src_indices) # these terms account for the flux in to the sink state # from the truncated domain data.append(ext_coefficients) cols.append(ext_src_indices) rows.append(ext_dst_indices) matrix_size = domain_enum.size if sink: matrix_size += 1 matrix_shape = (matrix_size, )*2 if len(data) == 0: reaction_matrix = scipy.sparse.csr_matrix(matrix_shape) else: # merge data, rows, cols data = numpy.concatenate(data) cols = numpy.concatenate(cols) rows = numpy.concatenate(rows) # create coo matrix coo_data = (data, (rows, cols)) reaction_matrix = scipy.sparse.coo_matrix(coo_data, matrix_shape) # convert to sparse csr format, then compress & optimise the storage reaction_matrix = reaction_matrix.tocsr() optimise_csr_matrix(reaction_matrix) yield reaction_matrix return
def create(model, sink, p_0=None, t_0=None, sink_0=None, time_dependencies=None, domain_states=None, solver=ode_solver.Solver, outflow=False, **solver_args): """ Returns a solver for the Chemical Master Equation of the given model. arguments: model : the CME model to solve sink : If sink is True, the solver will include a 'sink' state used to accumulate any probability that may flow outside the domain. This can be used to measure the error in the solution due to truncation of the domain. If sink is False, the solver will not include a 'sink' state, and probability will be artificially prevented from flowing outside of the domain. p_0 : (optional) mapping from states in the domain to probabilities, for the initial probability distribution. If not specified, and the initial state of the state space is given by the model, defaults to all probability concentrated at the initial state, otherwise, a ValueError will be raised. t_0 : (optional) initial time, defaults to 0.0 sink_0 : (optional) initial sink probability, defaults to 0.0 Only a valid argument if sink is set to True. time_dependencies : (optional) By default, reaction propensities are time independent. If specified, time_dependencies must be of the form { s_1 : phi_1, ..., s_n : phi_n }, where each (s_j, phi_j) item satisifes : s_j : set of reaction indices phi_j : phi_j(t) -> time dependent coefficient The propensities of the reactions with indicies contained in s_j will all be multiplied by the coefficient phi_j(t), at time t. Reactions are indexed according to the ordering of the propensities in the model. The reaction index sets s_j must be *disjoint*. It is not necessary for the union of the s_j to include all the reaction indices. If a reaction's index is not contained in any s_j then the reaction is treated as time-independent. mapping of time dependent coefficient functions keyed by subsets of reaction indices, with respect to the ordering of reactions determined by the order of the propensity functions inside the model. The propensities of the reactions with indices included in each subset are multiplied by the time dependent coefficient function. By default, no time dependent coefficient functions are specified, that is, the CME has time-independent propensities. domain_states : (optional) array of states in the domain. By default, generate the rectangular lattice of states defined by the 'shape' entry of the model. A ValueError is raised if both domain_states and 'shape' are unspecified. """ mdl.validate_model(model) if sink and outflow: raise ValueError('sink and outflow cannot be both True') if sink_0 is not None: if not sink: raise ValueError('sink_0 may not be specified if sink is False') sink_0 = float(sink_0) else: sink_0 = 0.0 # determine states in domain, then construct an enumeration of the # domain states if domain_states is None: if mdl.SHAPE not in model: lament = 'if no states given, model must contain key \'%s\'' raise KeyError(lament % mdl.SHAPE) else: domain_states = domain.from_rect(shape=model.shape) domain_enum = state_enum.create(domain_states) # determine p_0, then construct a dense representation with respect to # the domain enumeration initial_state = model.get(mdl.INITIAL_STATE, None) if p_0 is None: if initial_state is None: lament = 'if no p_0 given, model must contain key \'%s\'' raise ValueError(lament % mdl.INITIAL_STATE) else: p_0 = {initial_state: 1.0} if t_0 is None: t_0 = 0.0 member_flags = domain_enum.contains(domain.from_iter(p_0)) if not numpy.logical_and.reduce(member_flags): raise ValueError('support of p_0 is not a subset of domain_states') # compute reaction matrices and use them to define differential equations gen_matrices = cme_matrix.gen_reaction_matrices(model, domain_enum, sink, cme_matrix.non_neg_states, outflow=outflow) reaction_matrices = list(gen_matrices) dy_dt = cme_matrix.create_diff_eqs(reaction_matrices, phi=time_dependencies) if solver_args: solver_args['reaction_matrices'] = reaction_matrices # construct and initialise solver if sink: cme_solver = solver(dy_dt, y_0=(p_0, sink_0), t_0=t_0, **solver_args) pack, unpack = create_packing_functions(domain_enum) cme_solver.set_packing(pack, unpack, transform_dy_dt=False) else: pack = domain_enum.pack_distribution unpack = domain_enum.unpack_distribution cme_solver = solver(dy_dt, y_0=p_0, t_0=t_0, **solver_args) cme_solver.set_packing(pack, unpack, transform_dy_dt=False) return cme_solver