Skip to content

Commit

Permalink
Merge pull request #938 from StochSS/825--all_discrete_optimization
Browse files Browse the repository at this point in the history
Tau hybrid solvers c++ python need argument tau step
  • Loading branch information
seanebum authored Apr 27, 2023
2 parents 492b4c8 + 85dd8aa commit 04f1078
Show file tree
Hide file tree
Showing 13 changed files with 1,099 additions and 86 deletions.
814 changes: 814 additions & 0 deletions examples/Tau_Validation.ipynb

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -296,7 +296,7 @@ namespace Gillespy

// Helper method to flag reactions that can be processed deterministically (continuous change)
// without exceeding the user-supplied tolerance
std::set<int> flag_det_rxns(
int flag_det_rxns(
std::vector<HybridReaction> &reactions,
std::vector<HybridSpecies> &species)
{
Expand Down Expand Up @@ -337,7 +337,7 @@ namespace Gillespy
}
}

return det_rxns;
return det_rxns.size();
}

void partition_species(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -304,7 +304,7 @@ namespace Gillespy
};
};

std::set<int> flag_det_rxns(
int flag_det_rxns(
std::vector<HybridReaction> &reactions,
std::vector<HybridSpecies> &species);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,9 @@
#include "integrator.h"
#include "tau.h"

#include "template_defaults.h"


static void silent_error_handler(int error_code, const char *module, const char *function_name,
char *message, void *eh_data);

Expand All @@ -52,8 +55,7 @@ namespace Gillespy
{
void CalculateSpeciesChangeAfterStep(IntegrationResults&result, int*population_changes,
std::vector<double> current_state, std::set<unsigned int>&rxn_roots,
std::set<int>&event_roots, HybridSimulation*simulation, URNGenerator&urn,
int only_reaction_to_fire){
std::set<int>&event_roots, HybridSimulation*simulation, URNGenerator&urn){
Model<double> &model = *(simulation->model);
int num_species = model.number_species;
int num_reactions = model.number_reactions;
Expand Down Expand Up @@ -88,12 +90,13 @@ namespace Gillespy

if (simulation->reaction_state[rxn_i].mode == SimulationState::DISCRETE) {
unsigned int rxn_count = 0;
if(only_reaction_to_fire > -1){
if(only_reaction_to_fire == rxn_i){
rxn_state = log(urn.next());
rxn_count = 1;
}
}else if(rxn_state > 0){
//if(only_reaction_to_fire > -1){
// if(only_reaction_to_fire == rxn_i){
// rxn_state = log(urn.next());
// rxn_count = 1;
// }
//}else
if(rxn_state > 0){
std::poisson_distribution<int> poisson(rxn_state);
rxn_count = 1 + poisson(generator);
rxn_state = log(urn.next());
Expand All @@ -113,20 +116,24 @@ namespace Gillespy
bool TakeIntegrationStep(Integrator&sol, IntegrationResults&result, double *next_time, int*population_changes,
std::vector<double> current_state, std::set<unsigned int>&rxn_roots,
std::set<int>&event_roots, HybridSimulation*simulation, URNGenerator&urn,
int only_reaction_to_fire){
int num_det_rxns, int num_rate_rules){
// Integration Step

// check to see if we can do a constant integration (no deterministic reactions or rate rules)

// For deterministic reactions, the concentrations are updated directly.
// For stochastic reactions, integration updates the rxn_offsets vector.
result = sol.integrate(next_time, event_roots, rxn_roots);
result = sol.integrate(next_time, event_roots, rxn_roots, num_det_rxns, num_rate_rules);
if (sol.status == IntegrationStatus::BAD_STEP_SIZE)
{
simulation->set_status(HybridSimulation::INTEGRATOR_FAILED);
return false;
} else {
// The integrator has, at this point, been validated.
// Any errors beyond this point is assumed to be a stochastic state failure.
CalculateSpeciesChangeAfterStep(result, population_changes, current_state, rxn_roots, event_roots, simulation, urn, only_reaction_to_fire);
}


// The integrator has, at this point, been validated.
// Any errors beyond this point is assumed to be a stochastic state failure.
CalculateSpeciesChangeAfterStep(result, population_changes, current_state, rxn_roots, event_roots, simulation, urn);
return true;
}

Expand Down Expand Up @@ -171,6 +178,7 @@ namespace Gillespy
GPY_INTERRUPT_INSTALL_HANDLER(signal_handler);

Model<double> &model = *(simulation->model);
int num_rate_rules = 0;
int num_species = model.number_species;
int num_reactions = model.number_reactions;
int num_trajectories = simulation->number_trajectories;
Expand All @@ -182,6 +190,9 @@ namespace Gillespy
std::vector<int> non_negative_species;

for (int spec = 0; spec < model.number_species; spec++) {
HybridSpecies *specO = &simulation->species_state[spec];
num_rate_rules += specO->diff_equation.rate_rules.size();

for (int r = 0; r < model.number_reactions; r++) {
if (model.reactions[r].products_change[spec] > 0 ||
model.reactions[r].reactants_change[spec] > 0) {
Expand Down Expand Up @@ -282,6 +293,12 @@ namespace Gillespy
s_vars[s_num_i] = saved__s_variables[s_num_i];
}

// reset R_j values
double *curr_rxn_state = sol.get_reaction_state();
for (unsigned int rxn_j = 0; rxn_j < num_reactions; ++rxn_j) {
curr_rxn_state[rxn_j] = log(urn.next());
}


while (!interrupted && !invalid_state && simulation->current_time < simulation->end_time)
{
Expand Down Expand Up @@ -313,15 +330,19 @@ namespace Gillespy
}

// Expected tau step is determined.
tau_step = select<double, double>(
model,
tau_args,
tau_tol,
simulation->current_time,
save_time,
sol.data.propensities,
current_state
);
if(GPY_CONSTANT_TAU_STEPSIZE > 0){
tau_step = GPY_CONSTANT_TAU_STEPSIZE;
}else{
tau_step = select<double, double>(
model,
tau_args,
tau_tol,
simulation->current_time,
save_time,
sol.data.propensities,
current_state
);
}
partition_species(
simulation->current_time,
simulation->reaction_state,
Expand All @@ -331,7 +352,7 @@ namespace Gillespy
tau_step,
tau_args
);
flag_det_rxns(
int num_det_rxns = flag_det_rxns(
simulation->reaction_state,
simulation->species_state
);
Expand Down Expand Up @@ -371,7 +392,7 @@ namespace Gillespy
}


if(!TauHybrid::TakeIntegrationStep(sol, result, &next_time, population_changes, current_state, rxn_roots, event_roots, simulation, urn, -1)){
if(!TauHybrid::TakeIntegrationStep(sol, result, &next_time, population_changes, current_state, rxn_roots, event_roots, simulation, urn, num_det_rxns, num_rate_rules)){
return;
}

Expand Down
51 changes: 49 additions & 2 deletions gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/integrator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,47 @@ Integrator::~Integrator()
delete[] m_roots;
}

IntegrationResults Integrator::integrate_constant(double *t)
{
// this function assumes no deterministic species or
realtype *Y = N_VGetArrayPointer(y);
HybridSimulation *sim = data.simulation;
std::vector<HybridSpecies> *species = data.species_state;
std::vector<HybridReaction> *reactions = data.reaction_state;
std::vector<double> &propensities = data.propensities;
unsigned int num_species = sim->model->number_species;
unsigned int num_reactions = sim->model->number_reactions;
realtype propensity;
for (unsigned int rxn_i = 0; rxn_i < num_reactions; ++rxn_i)
{
HybridReaction rxn = (*reactions)[rxn_i];
switch (rxn.mode) {
case SimulationState::DISCRETE:
// Process stochastic reaction state by updating the root offset for each reaction.
propensity = rxn.ssa_propensity(Y);
propensities[rxn_i] = propensity;
break;

case SimulationState::CONTINUOUS:
break;
default:
break;
}
}

double tau = *t - this->t;
for (int rxn_i = 0; rxn_i < num_reactions; ++rxn_i){
NV_Ith_S(y, rxn_i+num_species) = NV_Ith_S(y, rxn_i+num_species) + propensities[rxn_i] * tau;
}
this->t = *t;

return {
NV_DATA_S(y),
NV_DATA_S(y) + num_species,
IntegrationStatus::OK
};
}

IntegrationResults Integrator::integrate(double *t)
{
int retcode = CVode(cvode_mem, *t, y, &this->t, CV_NORMAL);
Expand Down Expand Up @@ -157,9 +198,15 @@ void Integrator::reset_model_vector()
}
}

IntegrationResults Integrator::integrate(double *t, std::set<int> &event_roots, std::set<unsigned int> &reaction_roots)
IntegrationResults Integrator::integrate(double *t, std::set<int> &event_roots, std::set<unsigned int> &reaction_roots, int num_det_rxns, int num_rate_rules)
{
IntegrationResults results = integrate(t);

IntegrationResults results;
if(num_det_rxns == 0 && num_rate_rules == 0 && data.active_triggers.size() == 0 && data.active_reaction_ids.size() == 0){
results = integrate_constant(t);
}else{
results = integrate(t);
}
if (status != IntegrationStatus::OK) {
return results;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -196,7 +196,8 @@ namespace Gillespy
}

IntegrationResults integrate(double *t);
IntegrationResults integrate(double *t, std::set<int> &event_roots, std::set<unsigned int> &reaction_roots);
IntegrationResults integrate_constant(double *t);
IntegrationResults integrate(double *t, std::set<int> &event_roots, std::set<unsigned int> &reaction_roots, int num_det_rxns, int num_rate_rules);
IntegratorData data;

Integrator(HybridSimulation *simulation, Model<double> &model, URNGenerator urn, double reltol, double abstol);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@
#include "TauLeapingSolver.h"
#include "tau.h"

#include "template_defaults.h"

namespace Gillespy
{
static volatile bool interrupted = false;
Expand Down Expand Up @@ -149,8 +151,11 @@ namespace Gillespy
{
propensity_values[reaction_number] = Reaction::propensity(reaction_number, current_state.data());
}

tau_step = select(*(simulation->model), tau_args, tau_tol, simulation->current_time, save_time, propensity_values, current_state);
if(GPY_CONSTANT_TAU_STEPSIZE > 0){
tau_step = GPY_CONSTANT_TAU_STEPSIZE;
}else{
tau_step = select(*(simulation->model), tau_args, tau_tol, simulation->current_time, save_time, propensity_values, current_state);
}
prev_curr_state = current_state;
double prev_curr_time = simulation->current_time;
int loop_cnt = 0;
Expand Down
7 changes: 7 additions & 0 deletions gillespy2/solvers/cpp/c_base/template/template_defaults.h
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,11 @@
#define GPY_REACTION_NAMES
#endif


#ifndef GPY_CONSTANT_TAU_STEPSIZE
#define GPY_CONSTANT_TAU_STEPSIZE 0
#endif

// ===============================================================
// ================ HYBRID SOLVER OPTION DEFAULTS ================
// ===============================================================
Expand All @@ -77,6 +82,7 @@
* SPECIES_MODE(1, CONTINUOUS_MODE) \
* SPECIES_MODE(2, DYNAMIC_MODE)
*/

#ifdef GPY_SOLVER_HYBRID

#ifndef GPY_HYBRID_SPECIES_MODES
Expand All @@ -89,3 +95,4 @@

#endif


24 changes: 21 additions & 3 deletions gillespy2/solvers/cpp/tau_hybrid_c_solver.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,12 @@ class TauHybridCSolver(GillesPySolver, CSolver):
name = "TauHybridCSolver"
target = "hybrid"

def __init__(self, model = None, output_directory = None, delete_directory = True, resume=None, variable = False, constant_tau_stepsize=None):

self.constant_tau_stepsize = constant_tau_stepsize
super().__init__(model=model, output_directory=output_directory,
delete_directory=delete_directory, resume=resume, variable=variable)

class ErrorStatus(IntEnum):
UNKNOWN = 1
LOOP_OVER_INTEGRATE = 2
Expand All @@ -30,8 +36,7 @@ class ErrorStatus(IntEnum):
NEGATIVE_STATE_NO_SSA_REACTION = 5
NEGATIVE_STATE_AT_BEGINING_OF_STEP = 6

@classmethod
def __create_options(cls, sanitized_model: "SanitizedModel") -> "SanitizedModel":
def __create_options(self, sanitized_model: "SanitizedModel") -> "SanitizedModel":
"""
Populate the given list of species modes into a set of template macro definitions.
Generated options are specific to the Tau Hybrid solver,
Expand Down Expand Up @@ -169,14 +174,25 @@ def get_supported_features(cls):
def get_supported_integrator_options(cls) -> "Set[str]":
return { "rtol", "atol", "max_step" }



def _build(self, model: "Union[Model, SanitizedModel]", simulation_name: str, variable: bool, debug: bool = False,
custom_definitions=None) -> str:
variable = variable or len(model.listOfEvents) > 0
sanitized_model = TauHybridCSolver.__create_options(SanitizedModel(model, variable=variable))
sanitized_model = self.__create_options(SanitizedModel(model, variable=variable))
for rate_rule in model.listOfRateRules.values():
sanitized_model.use_rate_rule(rate_rule)

# determine if a constant stepsize has been requested
if self.constant_tau_stepsize is not None:
sanitized_model.options['GPY_CONSTANT_TAU_STEPSIZE'] = str(float(self.constant_tau_stepsize))
else:
sanitized_model.options['GPY_CONSTANT_TAU_STEPSIZE'] = '0'
return super()._build(sanitized_model, simulation_name, variable, debug)




def _handle_return_code(self, return_code: "int") -> "int":
if return_code == TauHybridCSolver.ErrorStatus.UNKNOWN:
raise SimulationError("C++ solver failed (no error code given).")
Expand Down Expand Up @@ -270,10 +286,12 @@ def run(self=None, model: Model = None, t: int = None, number_of_trajectories: i
raise SimulationError("A model is required to run the simulation.")
self._set_model(model=model)


self.model.compile_prep()
self.validate_model(self.model, model)
self.validate_sbml_features(model=self.model)


self.validate_tspan(increment=increment, t=t)
if increment is None:
increment = self.model.tspan[-1] - self.model.tspan[-2]
Expand Down
Loading

0 comments on commit 04f1078

Please sign in to comment.