From 6d680a787964e0f2cadfd22c6b5c063e5f67007c Mon Sep 17 00:00:00 2001 From: mdip226 Date: Mon, 1 Mar 2021 13:59:51 -0500 Subject: [PATCH 01/88] hybrid_compile --- gillespy2/solvers/cpp/tau_hybrid_c_solver.py | 254 +++++++++++++++++++ 1 file changed, 254 insertions(+) create mode 100644 gillespy2/solvers/cpp/tau_hybrid_c_solver.py diff --git a/gillespy2/solvers/cpp/tau_hybrid_c_solver.py b/gillespy2/solvers/cpp/tau_hybrid_c_solver.py new file mode 100644 index 000000000..1a49d0726 --- /dev/null +++ b/gillespy2/solvers/cpp/tau_hybrid_c_solver.py @@ -0,0 +1,254 @@ +import gillespy2 +from gillespy2.core import gillespyError, GillesPySolver, log +from gillespy2.solvers.utilities import solverutils as cutils +import signal +import time # for solver timeout implementation +import os # for getting directories for C++ files +import shutil # for deleting/copying files +import subprocess # For calling make and executing c solver +import tempfile # for temporary directories + +GILLESPY_PATH = os.path.dirname(os.path.abspath(__file__)) +GILLESPY_CPP_TAU_HYBRID_DIR = os.path.join(GILLESPY_PATH, 'c_base/tau_hybrid_cpp_solver') +MAKE_FILE = os.path.dirname(os.path.abspath( + __file__)) + '/c_base/tau_hybrid_cpp_solver/makefile' +SUNDIALS_DIR = os.path.join(GILLESPY_PATH, 'c_base/Sundials') +CBASE_DIR = os.path.join(GILLESPY_PATH, 'c_base/') + + +class TauHybridCSolver(GillesPySolver): + name = "TauHybridCSolver" + """TODO""" + + def __init__(self, model=None, output_directory=None, delete_directory=True, resume=None, variable=False): + super(TauHybridCSolver, self).__init__() + self.__compiled = False + self.delete_directory = False + self.model = model + self.resume = resume + self.variable = variable + if self.model is not None: + # Create constant, ordered lists for reactions/species/ + self.species_mappings = self.model.sanitized_species_names() + self.species = list(self.species_mappings.keys()) + self.parameter_mappings = self.model.sanitized_parameter_names() + self.parameters = list(self.parameter_mappings.keys()) + self.reactions = list(self.model.listOfReactions.keys()) + + if isinstance(output_directory, str): + output_directory = os.path.abspath(output_directory) + + if isinstance(output_directory, str): + if not os.path.isfile(output_directory): + self.output_directory = output_directory + self.delete_directory = delete_directory + if not os.path.isdir(output_directory): + os.makedirs(self.output_directory) + else: + raise gillespyError.DirectoryError( + "File exists with the same path as directory.") + else: + self.temporary_directory = tempfile.TemporaryDirectory() + self.output_directory = self.temporary_directory.name + + if not os.path.isdir(self.output_directory): + raise gillespyError.DirectoryError("Errors encountered while setting up directory for Solver C++ files." + ) + self.__write_template() + self.__compile() + + def __del__(self): + if self.delete_directory and os.path.isdir(self.output_directory): + shutil.rmtree(self.output_directory) + + def __write_template(self): + # Open up template file for reading. + + if self.variable: + template_file = "VariableTauHybridTemplate.cpp" + else: + template_file = "TauhybridTemplate.cpp" + + with open(os.path.join(GILLESPY_CPP_TAU_HYBRID_DIR, template_file), 'r') as template: + # Write simulation C++ file. + template_keyword = "__DEFINE_" + # Use same lists of model's species and reactions to maintain order + with open(os.path.join(self.output_directory, 'TauHybridSimulation.cpp'), 'w') as outfile: + for line in template: + if line.startswith(template_keyword): + line = line[len(template_keyword):] + if line.startswith("VARIABLES"): + cutils.write_variables(outfile, self.model, self.reactions, self.species, + self.parameter_mappings, self.resume, variable=self.variable) + if line.startswith("PROPENSITY"): + cutils.write_propensity(outfile, self.model, self.species_mappings, self.parameter_mappings, + self.reactions) + if line.startswith("REACTIONS"): + cutils.write_reactions( + outfile, self.model, self.reactions, self.species) + if self.variable: + if line.startswith("PARAMETER_UPDATES"): + cutils.update_parameters( + outfile, self.parameters, self.parameter_mappings) + else: + outfile.write(line) + + def __compile(self): + # Use makefile. + if self.resume: + if self.resume[0].model != self.model: + raise gillespyError.ModelError( + 'When resuming, one must not alter the model being resumed.') + try: + cmd = ["make", "-C", self.output_directory, '-f', MAKE_FILE, + 'TauHybridSimulation', 'GILLESPY_CPP_TAU_HYBRID_DIR=' + + GILLESPY_CPP_TAU_HYBRID_DIR, 'CBASE_DIR='+CBASE_DIR, + 'SUNDIALS_DIR='+SUNDIALS_DIR] + built = subprocess.run( + cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE) + except KeyboardInterrupt: + log.warning( + "Solver has been interrupted during compile time, unexpected behavior may occur.") + + if built.returncode == 0: + self.__compiled = True + else: + raise gillespyError.BuildError("Error encountered while compiling file:\nReturn code: " + "{0}.\nError:\n{1}\n{2}\n".format(built.returncode, + built.stdout.decode( + 'utf-8'), + built.stderr.decode('utf-8'))) + + def get_solver_settings(self): + """ + :return: Tuple of strings, denoting all keyword argument for this solvers run() method. + """ + return ('model', 't', 'number_of_trajectories', 'timeout', 'increment', 'seed', 'debug', 'profile', 'variables') + + def run(self=None, model=None, t=20, number_of_trajectories=1, timeout=0, + increment=0.05, seed=None, debug=False, profile=False, variables={}, resume=None, **kwargs): + + pause = False + + if resume is not None: + if t < resume['time'][-1]: + raise gillespyError.ExecutionError( + "'t' must be greater than previous simulations end time, or set in the run() method as the " + "simulations next end time") + + if self is None or self.model is None: + self = TauHybridCSolver(model, resume=resume) + + if len(kwargs) > 0: + for key in kwargs: + log.warning( + 'Unsupported keyword argument to {0} solver: {1}'.format(self.name, key)) + + unsupported_sbml_features = { + 'Rate Rules': len(model.listOfRateRules), + 'Assignment Rules': len(model.listOfAssignmentRules), + 'Events': len(model.listOfEvents), + 'Function Definitions': len(model.listOfFunctionDefinitions) + } + detected_features = [] + for feature, count in unsupported_sbml_features.items(): + if count: + detected_features.append(feature) + + if len(detected_features): + raise gillespyError.ModelError( + 'Could not run Model. SBML Feature: {} not supported by TauHybridCSolver.'.format(detected_features)) + + if not isinstance(variables, dict): + raise gillespyError.SimulationError( + 'argument to variables must be a dictionary.') + for v in variables.keys(): + if v not in self.species+self.parameters: + raise gillespyError.SimulationError('Argument to variable "{}" \ + is not a valid variable. Variables must be model species or parameters.'.format(v)) + + if self.__compiled: + if self.variable: # Is a variable simulation + populations = cutils.update_species_init_values( + model.listOfSpecies, self.species, variables, resume) + parameter_values = cutils.change_param_values( + model.listOfParameters, self.parameters, model.volume, variables) + + self.simulation_data = None + if resume is not None: + t = abs(t - int(resume['time'][-1])) + + number_timesteps = int(round(t/increment + 1)) + # Execute simulation. + + args = [os.path.join(self.output_directory, 'TauHybridSimulation'), + '-trajectories', str(number_of_trajectories), + '-timesteps', str(number_timesteps), + '-end', str(t), '-increment', str(increment)] + + if self.variable: + args.extend(['-initial_values', populations, + '-parameters', parameter_values]) + + if seed is not None: + if isinstance(seed, int): + args.append('-seed') + args.append(str(seed)) + else: + seed_int = int(seed) + if seed_int > 0: + args.append('-seed') + args.append(str(seed_int)) + else: + raise gillespyError.ModelError( + "seed must be a positive integer") + + # begin subprocess c simulation with timeout (default timeout=0 will not timeout) + with subprocess.Popen(args, stdout=subprocess.PIPE, start_new_session=True) as simulation: + try: + if timeout > 0: + stdout, stderr = simulation.communicate( + timeout=timeout) + else: + stdout, stderr = simulation.communicate() + return_code = simulation.wait() + except KeyboardInterrupt: + # send signal to the process group + os.killpg(simulation.pid, signal.SIGINT) + stdout, stderr = simulation.communicate() + pause = True + return_code = 33 + except subprocess.TimeoutExpired: + # send signal to the process group + os.killpg(simulation.pid, signal.SIGINT) + stdout, stderr = simulation.communicate() + pause = True + return_code = 33 + # Decode from byte, split by comma into array + stdout = stdout.decode('utf-8').split(',') + # Parse/return results + + if return_code in [0, 33]: + trajectory_base, timeStopped = cutils.parse_binary_output(number_of_trajectories, number_timesteps, + len(model.listOfSpecies), stdout, pause=pause) + if model.tspan[2] - model.tspan[1] == 1: + timeStopped = int(timeStopped) + + # Format results + self.simulation_data = [] + for trajectory in range(number_of_trajectories): + data = {'time': trajectory_base[trajectory, :, 0]} + for i in range(len(self.species)): + data[self.species[i]] = trajectory_base[trajectory, :, i + 1] + + self.simulation_data.append(data) + else: + raise gillespyError.ExecutionError("Error encountered while running simulation C++ file:" + "\nReturn code: {0}.\nError:\n{1}\n". + format(simulation.returncode, simulation.stderr)) + + if resume is not None or timeStopped != 0: + self.simulation_data = cutils.c_solver_resume( + timeStopped, self.simulation_data, t, resume=resume) + + return self.simulation_data, return_code From e5e62423f309078afb28729e95050854d16fb666 Mon Sep 17 00:00:00 2001 From: mdip226 Date: Sun, 7 Mar 2021 18:14:56 -0500 Subject: [PATCH 02/88] copying --- gillespy2/solvers/cpp/__init__.py | 3 +- .../TauHybridCSolver.cpp | 142 ++++++++++++++++++ .../tau_hybrid_cpp_solver/TauHybridCSolver.h | 9 ++ .../TauHybridTemplate.cpp | 99 ++++++++++++ .../VariableTauHybridTemplate.cpp | 111 ++++++++++++++ .../cpp/c_base/tau_hybrid_cpp_solver/makefile | 37 +++++ 6 files changed, 400 insertions(+), 1 deletion(-) create mode 100644 gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/TauHybridCSolver.cpp create mode 100644 gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/TauHybridCSolver.h create mode 100644 gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/TauHybridTemplate.cpp create mode 100644 gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/VariableTauHybridTemplate.cpp create mode 100644 gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/makefile diff --git a/gillespy2/solvers/cpp/__init__.py b/gillespy2/solvers/cpp/__init__.py index 7aef094ce..648da847c 100644 --- a/gillespy2/solvers/cpp/__init__.py +++ b/gillespy2/solvers/cpp/__init__.py @@ -1,5 +1,6 @@ from gillespy2.solvers.cpp.ssa_c_solver import SSACSolver from gillespy2.solvers.cpp.ode_c_solver import ODECSolver from gillespy2.solvers.cpp.tau_leaping_c_solver import TauLeapingCSolver +from gillespy2.solvers.cpp.tau_hybrid_c_solver import TauHybridCSolver -__all__ = ['SSACSolver', 'ODECSolver', 'TauLeapingCSolver'] +__all__ = ['SSACSolver', 'ODECSolver', 'TauLeapingCSolver', 'TauHybridCSolver'] diff --git a/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/TauHybridCSolver.cpp b/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/TauHybridCSolver.cpp new file mode 100644 index 000000000..888fe8fe5 --- /dev/null +++ b/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/TauHybridCSolver.cpp @@ -0,0 +1,142 @@ +#include +#include "cvode.h" // prototypes for CVODE fcts., consts. +#include "nvector_serial.h" // access to serial N_Vector +#include "sunlinsol_spgmr.h" //access to SPGMR SUNLinearSolver +#include "cvode_spils.h" // access to CVSpils interface +#include "sundials_types.h" // defs. of realtype, sunindextype +#include "sundials_math.h" // contains the macros ABS, SUNSQR, EXP +#include "ODECSolver.h" +#include "model.h" +using namespace Gillespy; + +#define NV_Ith_S(v,i) (NV_DATA_S(v)[i]) // Access to individual components of data array, of N len vector + +static int f(realtype t, N_Vector y, N_Vector y_dot, void *user_data); // forward declare function to be used to solve RHS of ODE + +struct UserData { + Gillespy::Simulation *my_sim; +}; + +void ODESolver(Gillespy::Simulation* simulation, double increment){ + int flag; // CVODE constants returned if bad output, or success output. + // Constants: CV_SUCCESS, + // CV_MEM_NULL: CVODE memory block not initialized through call to CVodeCreate + // CV_NO_MALLOC: The allocation function CVodeInit not called + // CV_ILL_Input: An input tolerance was negative + + + // Allocate memory for data to be passed to RHS of ODE + UserData *data = new UserData(); + data->my_sim = simulation; + // my_sim points to a Gillespy::Simulation struct + + realtype abstol = 1e-5; // real tolerance of system + realtype reltol = 1e-5; // absolute tolerance of system + + // Initial conditions + sunindextype N = (simulation -> model)->number_species; // length of problem, 'sunindextype' index's sundials N + // N_VECTOR is a custom vector type with various methods. API is located on Chapter 6 of CVode guide + + N_Vector y0; // Initialize initial condition vector as an N_Vector. + y0 = N_VNew_Serial(N); + + for(unsigned int species_number = 0; species_number < ((simulation -> model) -> number_species); species_number++){ + NV_Ith_S(y0, species_number) = (simulation -> model) -> species[species_number].initial_population; + simulation -> trajectoriesODE[0][0][species_number] = (simulation -> model) -> species[species_number].initial_population; + } // Add species initial conditions to 'y0', our "current state vector" + //Initialize CVODE solver object + void* cvode_mem = NULL; // create cvode object ptr + cvode_mem = CVodeCreate(CV_BDF); // CV_ADAMS for nonstiff, CV_BDF for stiff problems + + realtype t0 = 0; // time i.c, must use realtype + flag = CVodeInit(cvode_mem, f, t0, y0); // Initalize ODE Solver with allocated memory, RHS function, t0, and y0. + // Set tolerances defined in beginning + flag = CVodeSStolerances(cvode_mem, reltol, abstol); + + // Create solver object + SUNLinearSolver LS; + // Choose linear solver module + // SUNSPMR - Iterative Solver (compatible with serial, threadel, parallel, user supplied nvector) + // SunLinearSolver_SUNSPGMR(N_Vector y, int pretype, intm axl) + // N_Vector y = vector to be used in solver | int pretype = flag indicating desired precondition type. '0' = none + // int maxl = the number of Krylov basis vectors to use. Values <= 0 defaults to '5' + LS = SUNLinSol_SPGMR(y0, 0, 0); + + // Attach linear solver module + flag = CVodeSetUserData(cvode_mem, data); + // CVodeSetLinearSolver(cvode_mem, LS, J)) + // cvode_mem : pointer to CVODE memory block | LS : SUNLINSOL object to use for solving linear systems + // J : SUNMATRIX object as template for the Jacobian, default NULL if not applicable + flag = CVodeSetLinearSolver(cvode_mem, LS, NULL); + + // For each point at which output is desired, call + // ier = CVode(cvode_mem, tout, yout, &tret, itask). Here, itask specifies the return mode. The vector yout + // (which can be the same as the vector y0 above) will contain y(t). More details at 4.5.7 + realtype tout; // the next time at which computed solution is desired + realtype end_time = simulation->end_time; + realtype step_length = increment; + realtype tret = 0; // the time reached by the solver for output + + // realtype yout (in this program, called y0) : the computed solution vector + // int itask : flag indicating the job of the solver for the NEXT user step + // CV_NORMAL option causes the solver to take internal steps until it has reached or just passed the 'tout' + // parameter. The solver interpolates in order to return an approximate value of y(tout). + // Cvode() returns a vector, 'y0' (which is y(tout)), and corresponding variable value 't' = tret (return time). + // With CV_NORMAL, tret will be equal to tout, and y0 = y(tout) + int curr_time = 0; + for (tout = step_length; tout <= end_time; tout += step_length){ + flag = CVode(cvode_mem, tout, y0, &tret, CV_NORMAL); + curr_time+=1; + for (sunindextype species = 0; species < N; species++){ + simulation->trajectoriesODE[0][curr_time][(int)species] = NV_Ith_S(y0,species); + } + } + + // Deallocate memory from solution vec + N_VDestroy(y0); + + // Free solver mem + CVodeFree(&cvode_mem); + + // Free linear solver/matrix mem + SUNLinSolFree(LS); +} + + +static int f(realtype t, N_Vector y, N_Vector y_dot, void *user_data) { + // N_VGetArrayPointer returns a pointer to the data in the N_Vector class. + realtype *ydata = N_VGetArrayPointer(y); // pointer y vector + realtype *dydata = N_VGetArrayPointer(y_dot); // pointer ydot vec + UserData *sim_data; + sim_data = (UserData*) user_data; // void pointer magic + + std::vector curr_state; // create vector of curr_state doubles, sent to propensity_function->ODEEvaluate() + int number_species = sim_data->my_sim->model->number_species; // for readability + int number_reacs = sim_data->my_sim->model->number_reactions; // for readability + std::vector propensity; // Vector of propensities of type 'realtypes' (doubles used in SUNDIALS), + + for (sunindextype i = 0; i < number_species; i++){ + dydata[i] = 0; // Initialize change in y to '0' + curr_state.push_back(ydata[i]); // add values found in our curr_state vector, 'ydata' to our curr_state vector + // This vector is used for our propensity_function method "evaluate", defined in abstract in 'model.h' + } + + for (sunindextype rxn = 0; rxn < number_reacs; rxn++){ + // Calculate propensity for each reaction, at current state + propensity.push_back((sim_data->my_sim)->propensity_function->ODEEvaluate((int)rxn, curr_state)); + + for (sunindextype spec = 0; spec < (sim_data->my_sim)->model->number_species; spec++){ + // if species is a product of this reaction, add propensity fxn + if ((sim_data->my_sim)->model->reactions[rxn].species_change[spec] > 0){ + dydata[spec] += propensity[rxn]; + } + // else if this species is reactant, subtract propensity fxn + else if ((sim_data->my_sim)->model->reactions[rxn].species_change[spec] < 0){ + dydata[spec] -= propensity[rxn]; + } + } + } + + + return(0); +} diff --git a/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/TauHybridCSolver.h b/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/TauHybridCSolver.h new file mode 100644 index 000000000..cd6a9b3e7 --- /dev/null +++ b/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/TauHybridCSolver.h @@ -0,0 +1,9 @@ +#ifndef TAUHYBRIDCSOLVER_H +#define TAUHYBRIDCSOLVER_H +#include "model.h" + +using namespace Gillespy; +void TauHybridSolver(Gillespy::Simulation* simulation, double increment); + + +#endif \ No newline at end of file diff --git a/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/TauHybridTemplate.cpp b/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/TauHybridTemplate.cpp new file mode 100644 index 000000000..80be27ffe --- /dev/null +++ b/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/TauHybridTemplate.cpp @@ -0,0 +1,99 @@ +#include +#include +#include +#include +#include +#include +#include "model.h" +#include "TauHybridCSolver.h" +using namespace Gillespy; + +//Default values, replaced with command line args +unsigned int number_trajectories = 0; +unsigned int number_timesteps = 0; +int random_seed = 0; +double end_time = 100.0; +bool seed_time = true; +double increment = 0; + +//Default constants/variables +__DEFINE_VARIABLES__ + +class PropensityFunction : public IPropensityFunction{ +public: + +double ODEEvaluate(int reaction_number, const std::vector &S){ + switch(reaction_number){ + +__DEFINE_PROPENSITY__ + + default: //Error + return -1; + } + } + double TauEvaluate(unsigned int reaction_number, const std::vector &S){return 1.0;} + double evaluate(unsigned int reaction_number, unsigned int* S){return 1.0;} +}; + +int main(int argc, char* argv[]){ + std :: vector species_names(s_names, s_names + sizeof(s_names)/sizeof(std :: string)); + std :: vector species_populations(populations, populations + sizeof(populations)/sizeof(populations[0])); + std :: vector reaction_names(r_names, r_names + sizeof(r_names)/sizeof(std :: string)); + + Model model(species_names, species_populations, reaction_names); + + //Begin reaction species changes +__DEFINE_REACTIONS_ + //End reaction species changes + model.update_affected_reactions(); + + //Parse command line arguments + std :: string arg; + for(int i = 1; i < argc - 1; i++){ + arg = argv[i]; + if(argc > i+1 && arg.size() > 1 && arg[0] == '-'){ + std :: stringstream arg_stream(argv[i+1]); + switch(arg[1]){ + case 's': + arg_stream >> random_seed; + seed_time = false; + break; + case 'e': + arg_stream >> end_time; + break; + case 'i': + arg_stream >> increment; + break; + case 't': + if(arg[2] == 'r'){ + arg_stream >> number_trajectories; + }else if(arg[2] == 'i'){ + arg_stream >> number_timesteps; + } + break; + } + } + } + + if(seed_time){ + random_seed = time(NULL); + } + IPropensityFunction *propFun = new PropensityFunction(); + //Simulation INIT + Simulation simulation; + Model* modelptr; + modelptr = &model; + simulation.ISODE=1; + simulation.model = modelptr; + simulation.end_time = end_time; + simulation.random_seed = random_seed; + simulation.number_timesteps = number_timesteps; + simulation.number_trajectories = number_trajectories; + simulation.propensity_function = propFun; + simulationODEINIT(&model, simulation); + // Perform ODE // + ODESolver(&simulation,increment); + simulation.output_results_buffer(std :: cout); + delete propFun; + return 0; +} \ No newline at end of file diff --git a/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/VariableTauHybridTemplate.cpp b/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/VariableTauHybridTemplate.cpp new file mode 100644 index 000000000..3a7e467b0 --- /dev/null +++ b/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/VariableTauHybridTemplate.cpp @@ -0,0 +1,111 @@ +#include +#include +#include +#include +#include +#include +#include "model.h" +#include "ODECSolver.h" +using namespace Gillespy; + +//Default values, replaced with command line args +unsigned int number_trajectories = 0; +unsigned int number_timesteps = 0; +int random_seed = 0; +double end_time = 100.0; +bool seed_time = true; +double increment = 0; + +//Default constants/variables +__DEFINE_VARIABLES__ + +class PropensityFunction : public IPropensityFunction{ +public: + +double ODEEvaluate(int reaction_number, const std::vector &S){ + switch(reaction_number){ + +__DEFINE_PROPENSITY__ + + default: //Error + return -1; + } + } + double TauEvaluate(unsigned int reaction_number, const std::vector &S){return 1.0;} + double evaluate(unsigned int reaction_number, unsigned int* S){return 1.0;} +}; + +int main(int argc, char* argv[]){ + //Parse command line arguments + std :: string arg; + for(int i = 1; i < argc - 1; i++){ + arg = argv[i]; + if(argc > i+1 && arg.size() > 1 && arg[0] == '-'){ + std :: stringstream arg_stream(argv[i+1]); + switch(arg[1]){ + case 'i': + if (arg[3] == 'c'){ + arg_stream >> increment; + } + else if(arg[3] == 'i'){ + for(int j = 0; j < int(sizeof(populations)); j++){ + arg_stream >> populations[j]; + } + } + break; + case 'p': +__DEFINE_PARAMETER_UPDATES__ + break; + case 's': + arg_stream >> random_seed; + seed_time = false; + break; + case 'e': + arg_stream >> end_time; + break; + case 't': + if(arg[2] == 'r'){ + arg_stream >> number_trajectories; + }else if(arg[2] == 'i'){ + arg_stream >> number_timesteps; + } + break; + } + } + } + + + + std :: vector species_names(s_names, s_names + sizeof(s_names)/sizeof(std :: string)); + std :: vector species_populations(populations, populations + sizeof(populations)/sizeof(populations[0])); + std :: vector reaction_names(r_names, r_names + sizeof(r_names)/sizeof(std :: string)); + + Model model(species_names, species_populations, reaction_names); + + //Begin reaction species changes +__DEFINE_REACTIONS_ + //End reaction species changes + model.update_affected_reactions(); + + if(seed_time){ + random_seed = time(NULL); + } + IPropensityFunction *propFun = new PropensityFunction(); + //Simulation INIT + Simulation simulation; + Model* modelptr; + modelptr = &model; + simulation.ISODE=1; + simulation.model = modelptr; + simulation.end_time = end_time; + simulation.random_seed = random_seed; + simulation.number_timesteps = number_timesteps; + simulation.number_trajectories = number_trajectories; + simulation.propensity_function = propFun; + simulationODEINIT(&model, simulation); + // Perform ODE // + ODESolver(&simulation,increment); + simulation.output_results_buffer(std :: cout); + delete propFun; + return 0; +} \ No newline at end of file diff --git a/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/makefile b/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/makefile new file mode 100644 index 000000000..480a4574e --- /dev/null +++ b/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/makefile @@ -0,0 +1,37 @@ +CC=g++ +CFLAGS = -c -std=c++14 -Wall -O3 +LINKFLAGS = -L. -std=c++14 -Wall -O3 +SRCDIR = $(SUNDIALS_DIR)/src +INCDIR = $(SUNDIALS_DIR)/include + +SIMDEPS = model.h +SIMOBJ = model.o + +SUNDEPS = cvode_ls.h cvode_proj.h sundials_matrix.h sunmatrix_band.h sunmatrix_dense.h sundials_iterative.h sundials_nonlinearsolver.h \ +sunnonlinsol_newton.h sundials_nvector.h nvector_serial.h sundials_linearsolver.h ODECSolver.h cvode.h cvode_spils.h \ +sundials_types.h sundials_math.h sunlinsol_spgmr.h + +SUNOBJ = cvode_nls.o cvode_io.o sundials_iterative.o cvode_proj.o sundials_matrix.o sunmatrix_band.o sunmatrix_dense.o cvode_ls.o \ +sundials_linearsolver.o sundials_nonlinearsolver.o sundials_nvector_senswrapper.o sunnonlinsol_newton.o \ +sundials_nvector.o nvector_serial.o cvode.o cvode_spils.o sundials_math.o sunlinsol_spgmr.o + +model.o: + $(CC) -c -o model.o $(CBASE_DIR)/model.cpp $(CFLAGS) -I$(CBASE_DIR) + +%.o: $(SRCDIR)/%.c + $(CC) -c -o $@ $< $(CFLAGS) -I$(INCDIR) + +TauHybridSimulation.o: + $(CC) -c -o TauHybridSimulation.o TauHybridSimulation.cpp $(CFLAGS) -I$(CBASE_DIR) -I$(GILLESPY_CPP_TAU_HYBRID_DIR) + +TauHybridCSolver.o: + $(CC) -c -o TauHybridCSolver.o $(GILLESPY_CPP_TAU_HYBRID_DIR)/TauHybridCSolver.cpp $(CFLAGS) -I$(CBASE_DIR) -I$(SRCDIR) -I$(INCDIR) + +TauHybridSimulation: TauHybridCSolver.o TauHybridSimulation.o $(SUNOBJ) model.o + $(CC) -o TauHybridSimulation TauHybridCSolver.o TauHybridSimulation.o $(SUNOBJ) model.o $(LINKFLAGS) + +cleanSimulationODE: + rm -f TauHybridSimulation + +clean: + rm -f *.o *~ \ No newline at end of file From a096ec601c704c10c992c4aaec2d3412f4d70b8e Mon Sep 17 00:00:00 2001 From: Matthew Dippel Date: Sun, 14 Mar 2021 22:17:30 -0400 Subject: [PATCH 03/88] share tau library --- .../cpp/c_base/tau_hybrid_cpp_solver/Tau.cpp | 246 ++++++++++++++++++ .../TauHybridCSolver.cpp | 87 +------ .../tau_hybrid_cpp_solver/TauHybridCSolver.h | 6 +- .../tau_leaping_cpp_solver/tau_leaper.cpp | 1 + .../tau_leaping_cpp_solver/tau_leaper.h | 2 +- 5 files changed, 254 insertions(+), 88 deletions(-) create mode 100644 gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/Tau.cpp diff --git a/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/Tau.cpp b/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/Tau.cpp new file mode 100644 index 000000000..6281eea14 --- /dev/null +++ b/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/Tau.cpp @@ -0,0 +1,246 @@ +#include "tau_leaper.h" +#include //Included for mt19937 random number generator, poisson distribution +#include //Included for natural logarithm +#include //Included for timeout signal handling +#include // For testing output +#include +#include +#include +#include +#include +#include +#include + +namespace Gillespy +{ + bool interrupted = false; + + void signalHandler(int signum) + { + interrupted = true; + } + struct TauArgs + { + //Highest Order Reaction + std::map HOR; + std::set reactants; + //Below are g_i_lambdas, pop element when used + std::map> g_i_lambdas; + std::map g_i; + std::map epsilon_i; + std::map> reactions_reactants; + std::map> products; + int critical_threshold = 10; + }; + + TauArgs initialize(Gillespy::Model &model, double tau_tol) + { + + // Initialize TauArgs struct to be returned as a pointer + TauArgs tau_args; + // Initialize highest order rxns to 0 + for (int i = 0; i < model.number_species; i++) + { + tau_args.HOR[model.species[i].name] = 0; + } + + for (int r = 0; r < model.number_reactions; r++) + { + int rxn_order = 0; + for (int spec = 0; spec < model.number_species; spec++) + { + if (model.reactions[r].species_change[spec] > 0) + tau_args.products[r].push_back(spec); + else if (model.reactions[r].species_change[spec] < 0) + { + rxn_order += 1; + tau_args.reactions_reactants[r].push_back(spec); + tau_args.reactants.insert(model.species[spec]); + } + } + + // if this reaction's order is higher than previous, set + if (tau_args.reactions_reactants[r].size() > 0) + { + for (auto const &reactant : tau_args.reactions_reactants[r]) + { + if (rxn_order > tau_args.HOR[model.species[reactant].name]) + { + tau_args.HOR[model.species[reactant].name] = rxn_order; + tau_args.g_i[model.species[reactant].name] = tau_args.HOR[model.species[reactant].name]; + + int count = std::abs(model.reactions[r].species_change[reactant]); + if (count == 2 && rxn_order == 2) + { + auto lambda = [](double x) { return (2 + (1 / (x - 1))); }; + tau_args.g_i_lambdas[model.species[reactant].name] = lambda; + } + else if (count == 2 && rxn_order == 3) + { + auto lambda = [](double x) { return ((3 / 2) * (2 + (1 / (x - 1)))); }; + tau_args.g_i_lambdas[model.species[reactant].name] = lambda; + } + else if (count == 3) + { + auto lambda = [](double x) { return (3 + (1 / (x - 1)) + (2 / (x - 2))); }; + tau_args.g_i_lambdas[model.species[reactant].name] = lambda; + } + else + { + tau_args.g_i[model.species[reactant].name] = tau_args.HOR[model.species[reactant].name]; + tau_args.epsilon_i[model.species[reactant].name] = tau_tol / tau_args.g_i[model.species[reactant].name]; + } + } + } + } + } + + return tau_args; + } + + double select(Gillespy::Model &model, TauArgs &tau_args, const double &tau_tol, const double ¤t_time, const double &save_time, const std::vector &propensity_values, const std::vector ¤t_state) + { + double tau; //tau time to step; + std::map critical_taus; //Mapping of possible critical_taus, to be evaluated + std::map mu_i; + std::map sigma_i; + bool critical = false; // system-wide flag, true when any reaction is critical + double non_critical_tau = 0; // holds the smallest tau time for non-critical reactions + double critical_tau = 0; // holds the smallest tau time for critical reactions + + int v; //used for number of population reactant consumes + + // initialize mu_i and sigma_i to 0 + for (int spec = 0; spec < model.number_species; spec++) + { + mu_i[model.species[spec].name] = 0; + sigma_i[model.species[spec].name] = 0; + } + // Determine if there are any critical reactions, update mu_i and sigma_i + for (int reaction = 0; reaction < model.number_reactions; reaction++) + { + for (auto const &reactant : tau_args.reactions_reactants[reaction]) + { + if (model.reactions[reaction].species_change[reactant] < 0) + { + v = abs(model.reactions[reaction].species_change[reactant]); + + if ((double)current_state[reactant] / v < tau_args.critical_threshold && propensity_values[reaction] > 0) + { + critical = true; // Critical reaction present in simulation + } + int consumed = abs(model.reactions[reaction].species_change[reactant]); + mu_i[model.species[reactant].name] += consumed * propensity_values[reaction]; //Cao, Gillespie, Petzold 32a + sigma_i[model.species[reactant].name] += std::pow(consumed, 2) * propensity_values[reaction]; //Cao, Gillespie, Petzold 32a + } + } + } + + // If a critical reaction is present, estimate tau for a single firing of each + // critical reaction with propensity > 0, and take the smallest tau + if (critical == true) + { + for (int reaction = 0; reaction < model.number_reactions; reaction++) + { + if (propensity_values[reaction] > 0) + critical_taus[model.reactions[reaction].name] = 1 / propensity_values[reaction]; + } + std::pair min; + //find min of critical_taus + min = *min_element(critical_taus.begin(), critical_taus.end(), [](const auto &lhs, const auto &rhs) { return lhs.second < rhs.second; }); + critical_tau = min.second; + } + + if (tau_args.g_i_lambdas.size() > 0) + { + for (auto const &x : tau_args.g_i_lambdas) + { + tau_args.g_i[x.first] = tau_args.g_i_lambdas[x.first](tau_args.g_i[x.first]); + tau_args.epsilon_i[x.first] = tau_tol / tau_args.g_i[x.first]; + tau_args.g_i_lambdas.erase(x.first); //MAYBE SHOULDN'T ERASE, may break loop/may be understanding this part wrong + } + } + + std::map tau_i; //Mapping of possible non-critical_taus, to be evaluated + + for (const auto &r : tau_args.reactants) + { + double calculated_max = tau_args.epsilon_i[r.name] * current_state[r.id]; + double max_pop_change_mean = std::max(calculated_max, 1.0); + double max_pop_change_sd = pow(max_pop_change_mean, 2); + if (mu_i[r.name] > 0) + { // Cao, Gillespie, Petzold 33 + tau_i[r.name] = std::min(std::abs(max_pop_change_mean / mu_i[r.name]), max_pop_change_sd / sigma_i[r.name]); + } + } + + if (tau_i.size() > 0) + { + std::pair min; + //find min of tau_i + min = *min_element(tau_i.begin(), tau_i.end(), [](const auto &lhs, const auto &rhs) { return lhs.second < rhs.second; }); + non_critical_tau = min.second; + } + + // If all reactions are non-critical, use non-critical tau. + if (critical == false) + { + tau = non_critical_tau; + } + // If all reactions are critical, use critical tau. + else if (tau_i.size() == 0) + { + tau = critical_tau; + } + // If there are both critical, and non critical reactions, + // Take the shortest tau between critica and non-critical. + else + { + tau = std::min(non_critical_tau, critical_tau); + } + // If selected tau exceeds save time, integrate to save time + if (tau > 0) + { + tau = std::max(tau, 1e-10); + if (save_time - current_time > 0) + { + tau = std::min(tau, save_time - current_time); + } + } + else + { + tau = save_time - current_time; + } + + return tau; + } + + std::pair, double> get_reactions(const Gillespy::Model *model, const std::vector &propensity_values, double tau_step, double current_time, double save_time) + { + /* + * Helper Function to get reactions fired from t to t+tau. Effects two values: + *rxn_count - dict with key=Reaction channel value=number of times fired + *curr_time - float representing current time + */ + + if (current_time + tau_step > save_time) + tau_step = save_time - current_time; + + std::map rxn_count; // map of how many times reaction is fired + std::random_device rd; + std::mt19937 generator(rd()); + std::pair, double> values; // value pair to be returned, map of times {map of times reaction fired, current time} + + for (int i = 0; i < model->number_reactions; i++) + { + std::poisson_distribution poisson(propensity_values[i] * tau_step); + rxn_count[model->reactions[i].name] = poisson(generator); + } + current_time = current_time + tau_step; + values.first = rxn_count; + values.second = current_time; + return values; + } + + +} diff --git a/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/TauHybridCSolver.cpp b/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/TauHybridCSolver.cpp index 888fe8fe5..e73264962 100644 --- a/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/TauHybridCSolver.cpp +++ b/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/TauHybridCSolver.cpp @@ -5,7 +5,7 @@ #include "cvode_spils.h" // access to CVSpils interface #include "sundials_types.h" // defs. of realtype, sunindextype #include "sundials_math.h" // contains the macros ABS, SUNSQR, EXP -#include "ODECSolver.h" +#include "TauHybridCSolver.h" #include "model.h" using namespace Gillespy; @@ -17,89 +17,8 @@ struct UserData { Gillespy::Simulation *my_sim; }; -void ODESolver(Gillespy::Simulation* simulation, double increment){ - int flag; // CVODE constants returned if bad output, or success output. - // Constants: CV_SUCCESS, - // CV_MEM_NULL: CVODE memory block not initialized through call to CVodeCreate - // CV_NO_MALLOC: The allocation function CVodeInit not called - // CV_ILL_Input: An input tolerance was negative - - - // Allocate memory for data to be passed to RHS of ODE - UserData *data = new UserData(); - data->my_sim = simulation; - // my_sim points to a Gillespy::Simulation struct - - realtype abstol = 1e-5; // real tolerance of system - realtype reltol = 1e-5; // absolute tolerance of system - - // Initial conditions - sunindextype N = (simulation -> model)->number_species; // length of problem, 'sunindextype' index's sundials N - // N_VECTOR is a custom vector type with various methods. API is located on Chapter 6 of CVode guide - - N_Vector y0; // Initialize initial condition vector as an N_Vector. - y0 = N_VNew_Serial(N); - - for(unsigned int species_number = 0; species_number < ((simulation -> model) -> number_species); species_number++){ - NV_Ith_S(y0, species_number) = (simulation -> model) -> species[species_number].initial_population; - simulation -> trajectoriesODE[0][0][species_number] = (simulation -> model) -> species[species_number].initial_population; - } // Add species initial conditions to 'y0', our "current state vector" - //Initialize CVODE solver object - void* cvode_mem = NULL; // create cvode object ptr - cvode_mem = CVodeCreate(CV_BDF); // CV_ADAMS for nonstiff, CV_BDF for stiff problems - - realtype t0 = 0; // time i.c, must use realtype - flag = CVodeInit(cvode_mem, f, t0, y0); // Initalize ODE Solver with allocated memory, RHS function, t0, and y0. - // Set tolerances defined in beginning - flag = CVodeSStolerances(cvode_mem, reltol, abstol); - - // Create solver object - SUNLinearSolver LS; - // Choose linear solver module - // SUNSPMR - Iterative Solver (compatible with serial, threadel, parallel, user supplied nvector) - // SunLinearSolver_SUNSPGMR(N_Vector y, int pretype, intm axl) - // N_Vector y = vector to be used in solver | int pretype = flag indicating desired precondition type. '0' = none - // int maxl = the number of Krylov basis vectors to use. Values <= 0 defaults to '5' - LS = SUNLinSol_SPGMR(y0, 0, 0); - - // Attach linear solver module - flag = CVodeSetUserData(cvode_mem, data); - // CVodeSetLinearSolver(cvode_mem, LS, J)) - // cvode_mem : pointer to CVODE memory block | LS : SUNLINSOL object to use for solving linear systems - // J : SUNMATRIX object as template for the Jacobian, default NULL if not applicable - flag = CVodeSetLinearSolver(cvode_mem, LS, NULL); - - // For each point at which output is desired, call - // ier = CVode(cvode_mem, tout, yout, &tret, itask). Here, itask specifies the return mode. The vector yout - // (which can be the same as the vector y0 above) will contain y(t). More details at 4.5.7 - realtype tout; // the next time at which computed solution is desired - realtype end_time = simulation->end_time; - realtype step_length = increment; - realtype tret = 0; // the time reached by the solver for output - - // realtype yout (in this program, called y0) : the computed solution vector - // int itask : flag indicating the job of the solver for the NEXT user step - // CV_NORMAL option causes the solver to take internal steps until it has reached or just passed the 'tout' - // parameter. The solver interpolates in order to return an approximate value of y(tout). - // Cvode() returns a vector, 'y0' (which is y(tout)), and corresponding variable value 't' = tret (return time). - // With CV_NORMAL, tret will be equal to tout, and y0 = y(tout) - int curr_time = 0; - for (tout = step_length; tout <= end_time; tout += step_length){ - flag = CVode(cvode_mem, tout, y0, &tret, CV_NORMAL); - curr_time+=1; - for (sunindextype species = 0; species < N; species++){ - simulation->trajectoriesODE[0][curr_time][(int)species] = NV_Ith_S(y0,species); - } - } - - // Deallocate memory from solution vec - N_VDestroy(y0); - - // Free solver mem - CVodeFree(&cvode_mem); - - // Free linear solver/matrix mem - SUNLinSolFree(LS); +void TauHybridCSolver(Gillespy::Simulation* simulation, double increment){ + } diff --git a/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/TauHybridCSolver.h b/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/TauHybridCSolver.h index cd6a9b3e7..dc81635f1 100644 --- a/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/TauHybridCSolver.h +++ b/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/TauHybridCSolver.h @@ -2,8 +2,8 @@ #define TAUHYBRIDCSOLVER_H #include "model.h" -using namespace Gillespy; -void TauHybridSolver(Gillespy::Simulation* simulation, double increment); - +namespace Gillespy { + void TauHybridSolver(Gillespy::Simulation* simulation, const double tau_tol); +} #endif \ No newline at end of file diff --git a/gillespy2/solvers/cpp/c_base/tau_leaping_cpp_solver/tau_leaper.cpp b/gillespy2/solvers/cpp/c_base/tau_leaping_cpp_solver/tau_leaper.cpp index 70e14de37..71609a4f3 100644 --- a/gillespy2/solvers/cpp/c_base/tau_leaping_cpp_solver/tau_leaper.cpp +++ b/gillespy2/solvers/cpp/c_base/tau_leaping_cpp_solver/tau_leaper.cpp @@ -18,6 +18,7 @@ void signalHandler(int signum){ interrupted = true; } struct TauArgs{ + //Highest Order Reaction std::map HOR; std::set reactants; //Below are g_i_lambdas, pop element when used diff --git a/gillespy2/solvers/cpp/c_base/tau_leaping_cpp_solver/tau_leaper.h b/gillespy2/solvers/cpp/c_base/tau_leaping_cpp_solver/tau_leaper.h index fe1db775a..f665cee63 100644 --- a/gillespy2/solvers/cpp/c_base/tau_leaping_cpp_solver/tau_leaper.h +++ b/gillespy2/solvers/cpp/c_base/tau_leaping_cpp_solver/tau_leaper.h @@ -3,7 +3,7 @@ #include "model.h" namespace Gillespy{ -void tau_leaper(Gillespy::Simulation* simulation, const double tau_tol); + void tau_leaper(Gillespy::Simulation* simulation, const double tau_tol); } #endif // TAU_LEAPER_H From 3818e85caa46d352023c3c173a666e0ef17371e9 Mon Sep 17 00:00:00 2001 From: mdip226 Date: Mon, 15 Mar 2021 11:48:45 -0400 Subject: [PATCH 04/88] quicksave --- gillespy2/solvers/cpp/c_base/Tau/tau.cpp | 187 ++++++++++++++++++ gillespy2/solvers/cpp/c_base/Tau/tau.h | 32 +++ .../c_base/tau_leaping_cpp_solver/makefile | 7 +- .../tau_leaping_cpp_solver/tau_leaper.cpp | 167 +--------------- .../tau_leaping_cpp_solver/tau_leaper.h | 2 +- 5 files changed, 227 insertions(+), 168 deletions(-) create mode 100644 gillespy2/solvers/cpp/c_base/Tau/tau.cpp create mode 100644 gillespy2/solvers/cpp/c_base/Tau/tau.h diff --git a/gillespy2/solvers/cpp/c_base/Tau/tau.cpp b/gillespy2/solvers/cpp/c_base/Tau/tau.cpp new file mode 100644 index 000000000..ad8b2281e --- /dev/null +++ b/gillespy2/solvers/cpp/c_base/Tau/tau.cpp @@ -0,0 +1,187 @@ +#include "tau.h" + +#include + +namespace Gillespy { + TauArgs initialize(Gillespy::Model &model, double tau_tol) + { + + // Initialize TauArgs struct to be returned as a pointer + TauArgs tau_args; + // Initialize highest order rxns to 0 + for (int i = 0; i < model.number_species; i++) + { + tau_args.HOR[model.species[i].name] = 0; + } + + for (int r = 0; r < model.number_reactions; r++) + { + int rxn_order = 0; + for (int spec = 0; spec < model.number_species; spec++) + { + if (model.reactions[r].species_change[spec] > 0) + tau_args.products[r].push_back(spec); + else if (model.reactions[r].species_change[spec] < 0) + { + rxn_order += 1; + tau_args.reactions_reactants[r].push_back(spec); + tau_args.reactants.insert(model.species[spec]); + } + } + + // if this reaction's order is higher than previous, set + if (tau_args.reactions_reactants[r].size() > 0) + { + for (auto const &reactant : tau_args.reactions_reactants[r]) + { + if (rxn_order > tau_args.HOR[model.species[reactant].name]) + { + tau_args.HOR[model.species[reactant].name] = rxn_order; + tau_args.g_i[model.species[reactant].name] = tau_args.HOR[model.species[reactant].name]; + + int count = std::abs(model.reactions[r].species_change[reactant]); + if (count == 2 && rxn_order == 2) + { + auto lambda = [](double x) { return (2 + (1 / (x - 1))); }; + tau_args.g_i_lambdas[model.species[reactant].name] = lambda; + } + else if (count == 2 && rxn_order == 3) + { + auto lambda = [](double x) { return ((3 / 2) * (2 + (1 / (x - 1)))); }; + tau_args.g_i_lambdas[model.species[reactant].name] = lambda; + } + else if (count == 3) + { + auto lambda = [](double x) { return (3 + (1 / (x - 1)) + (2 / (x - 2))); }; + tau_args.g_i_lambdas[model.species[reactant].name] = lambda; + } + else + { + tau_args.g_i[model.species[reactant].name] = tau_args.HOR[model.species[reactant].name]; + tau_args.epsilon_i[model.species[reactant].name] = tau_tol / tau_args.g_i[model.species[reactant].name]; + } + } + } + } + } + + return tau_args; + } + + double select(Gillespy::Model &model, TauArgs &tau_args, const double &tau_tol, const double ¤t_time, const double &save_time, const std::vector &propensity_values, const std::vector ¤t_state) + { + double tau; //tau time to step; + std::map critical_taus; //Mapping of possible critical_taus, to be evaluated + std::map mu_i; + std::map sigma_i; + bool critical = false; // system-wide flag, true when any reaction is critical + double non_critical_tau = 0; // holds the smallest tau time for non-critical reactions + double critical_tau = 0; // holds the smallest tau time for critical reactions + + int v; //used for number of population reactant consumes + + // initialize mu_i and sigma_i to 0 + for (int spec = 0; spec < model.number_species; spec++) + { + mu_i[model.species[spec].name] = 0; + sigma_i[model.species[spec].name] = 0; + } + // Determine if there are any critical reactions, update mu_i and sigma_i + for (int reaction = 0; reaction < model.number_reactions; reaction++) + { + for (auto const &reactant : tau_args.reactions_reactants[reaction]) + { + if (model.reactions[reaction].species_change[reactant] < 0) + { + v = abs(model.reactions[reaction].species_change[reactant]); + + if ((double)current_state[reactant] / v < tau_args.critical_threshold && propensity_values[reaction] > 0) + { + critical = true; // Critical reaction present in simulation + } + int consumed = abs(model.reactions[reaction].species_change[reactant]); + mu_i[model.species[reactant].name] += consumed * propensity_values[reaction]; //Cao, Gillespie, Petzold 32a + sigma_i[model.species[reactant].name] += std::pow(consumed, 2) * propensity_values[reaction]; //Cao, Gillespie, Petzold 32a + } + } + } + + // If a critical reaction is present, estimate tau for a single firing of each + // critical reaction with propensity > 0, and take the smallest tau + if (critical == true) + { + for (int reaction = 0; reaction < model.number_reactions; reaction++) + { + if (propensity_values[reaction] > 0) + critical_taus[model.reactions[reaction].name] = 1 / propensity_values[reaction]; + } + std::pair min; + //find min of critical_taus + min = *min_element(critical_taus.begin(), critical_taus.end(), [](const auto &lhs, const auto &rhs) { return lhs.second < rhs.second; }); + critical_tau = min.second; + } + + if (tau_args.g_i_lambdas.size() > 0) + { + for (auto const &x : tau_args.g_i_lambdas) + { + tau_args.g_i[x.first] = tau_args.g_i_lambdas[x.first](tau_args.g_i[x.first]); + tau_args.epsilon_i[x.first] = tau_tol / tau_args.g_i[x.first]; + tau_args.g_i_lambdas.erase(x.first); //MAYBE SHOULDN'T ERASE, may break loop/may be understanding this part wrong + } + } + + std::map tau_i; //Mapping of possible non-critical_taus, to be evaluated + + for (const auto &r : tau_args.reactants) + { + double calculated_max = tau_args.epsilon_i[r.name] * current_state[r.id]; + double max_pop_change_mean = std::max(calculated_max, 1.0); + double max_pop_change_sd = pow(max_pop_change_mean, 2); + if (mu_i[r.name] > 0) + { // Cao, Gillespie, Petzold 33 + tau_i[r.name] = std::min(std::abs(max_pop_change_mean / mu_i[r.name]), max_pop_change_sd / sigma_i[r.name]); + } + } + + if (tau_i.size() > 0) + { + std::pair min; + //find min of tau_i + min = *min_element(tau_i.begin(), tau_i.end(), [](const auto &lhs, const auto &rhs) { return lhs.second < rhs.second; }); + non_critical_tau = min.second; + } + + // If all reactions are non-critical, use non-critical tau. + if (critical == false) + { + tau = non_critical_tau; + } + // If all reactions are critical, use critical tau. + else if (tau_i.size() == 0) + { + tau = critical_tau; + } + // If there are both critical, and non critical reactions, + // Take the shortest tau between critica and non-critical. + else + { + tau = std::min(non_critical_tau, critical_tau); + } + // If selected tau exceeds save time, integrate to save time + if (tau > 0) + { + tau = std::max(tau, 1e-10); + if (save_time - current_time > 0) + { + tau = std::min(tau, save_time - current_time); + } + } + else + { + tau = save_time - current_time; + } + + return tau; + } +} \ No newline at end of file diff --git a/gillespy2/solvers/cpp/c_base/Tau/tau.h b/gillespy2/solvers/cpp/c_base/Tau/tau.h new file mode 100644 index 000000000..349a2341b --- /dev/null +++ b/gillespy2/solvers/cpp/c_base/Tau/tau.h @@ -0,0 +1,32 @@ +#ifndef TAU_H +#define TAU_H + +#include "model.h" + +#include +#include +#include +#include + + +namespace Gillespy { + struct TauArgs + { + //Highest Order Reaction + std::map HOR; + std::set reactants; + //Below are g_i_lambdas, pop element when used + std::map> g_i_lambdas; + std::map g_i; + std::map epsilon_i; + std::map> reactions_reactants; + std::map> products; + int critical_threshold = 10; + }; + TauArgs initialize(Gillespy::Model &model, double tau_tol); + + double select(Gillespy::Model &model, TauArgs &tau_args, const double &tau_tol, const double ¤t_time, const double &save_time, const std::vector &propensity_values, const std::vector ¤t_state); + + +} +#endif //TAU_H \ No newline at end of file diff --git a/gillespy2/solvers/cpp/c_base/tau_leaping_cpp_solver/makefile b/gillespy2/solvers/cpp/c_base/tau_leaping_cpp_solver/makefile index 15458b306..0b874b177 100644 --- a/gillespy2/solvers/cpp/c_base/tau_leaping_cpp_solver/makefile +++ b/gillespy2/solvers/cpp/c_base/tau_leaping_cpp_solver/makefile @@ -1,8 +1,8 @@ CC=g++ CFLAGS = -c -std=c++14 -Wall -O3 LINKFLAGS = -L. -std=c++14 -Wall -O3 -DEPS = model.h tau_leaper.h -OBJ = tau_leaper.o model.o +DEPS = model.h tau_leaper.h tau.h +OBJ = tau_leaper.o model.o tau.o model.o: $(CC) -c -o model.o $(CBASE_DIR)/model.cpp $(CFLAGS) -I$(CBASE_DIR) @@ -10,6 +10,9 @@ model.o: tau_leaper.o: $(CC) -c -o tau_leaper.o $(GILLESPY_CPP_TAU_DIR)/tau_leaper.cpp $(CFLAGS) -I$(CBASE_DIR) -I$(GILLESPY_CPP_TAU_DIR) +tau.o: + $(CC) -c -o tau.o $(CBASE_DIR)/Tau/tau.cpp $(CFLAGS) -I$(CBASE_DIR) + TauSimulation.o: $(CC) -c -o TauSimulation.o TauSimulation.cpp $(CFLAGS) -I$(CBASE_DIR) -I$(GILLESPY_CPP_TAU_DIR) diff --git a/gillespy2/solvers/cpp/c_base/tau_leaping_cpp_solver/tau_leaper.cpp b/gillespy2/solvers/cpp/c_base/tau_leaping_cpp_solver/tau_leaper.cpp index 71609a4f3..86019b3d5 100644 --- a/gillespy2/solvers/cpp/c_base/tau_leaping_cpp_solver/tau_leaper.cpp +++ b/gillespy2/solvers/cpp/c_base/tau_leaping_cpp_solver/tau_leaper.cpp @@ -1,4 +1,5 @@ #include "tau_leaper.h" +#include "tau.h" #include //Included for mt19937 random number generator, poisson distribution #include //Included for natural logarithm #include //Included for timeout signal handling @@ -9,7 +10,7 @@ #include #include #include -#include + namespace Gillespy{ bool interrupted = false; @@ -17,176 +18,12 @@ bool interrupted = false; void signalHandler(int signum){ interrupted = true; } -struct TauArgs{ - //Highest Order Reaction - std::map HOR; - std::set reactants; - //Below are g_i_lambdas, pop element when used - std::map> g_i_lambdas; - std::map g_i; - std::map epsilon_i; - std::map> reactions_reactants; - std::map> products; - int critical_threshold = 10; -}; - -TauArgs initialize(Gillespy::Model &model, double tau_tol){ - - // Initialize TauArgs struct to be returned as a pointer - TauArgs tau_args; - // Initialize highest order rxns to 0 - for (int i=0; i0) - tau_args.products[r].push_back(spec); - else if (model.reactions[r].species_change[spec]<0){ - rxn_order +=1; - tau_args.reactions_reactants[r].push_back(spec); - tau_args.reactants.insert(model.species[spec]); - } - } - - // if this reaction's order is higher than previous, set - if (tau_args.reactions_reactants[r].size()>0){ - for (auto const &reactant:tau_args.reactions_reactants[r]){ - if (rxn_order > tau_args.HOR[model.species[reactant].name]){ - tau_args.HOR[model.species[reactant].name] = rxn_order; - tau_args.g_i[model.species[reactant].name] = tau_args.HOR[model.species[reactant].name]; - - int count = std::abs(model.reactions[r].species_change[reactant]); - if (count == 2 && rxn_order == 2){ - auto lambda = [](double x) {return (2 + (1 / (x - 1)));}; - tau_args.g_i_lambdas[model.species[reactant].name] = lambda; - } - else if(count == 2 && rxn_order == 3){ - auto lambda = [](double x) {return ((3 / 2) * (2 + (1 / (x - 1))));}; - tau_args.g_i_lambdas[model.species[reactant].name] = lambda; - } - else if (count == 3){ - auto lambda = [](double x) {return (3 + (1 / (x - 1)) + (2 / (x - 2)));}; - tau_args.g_i_lambdas[model.species[reactant].name] = lambda; - } - else{ - tau_args.g_i[model.species[reactant].name] = tau_args.HOR[model.species[reactant].name]; - tau_args.epsilon_i[model.species[reactant].name] = tau_tol / tau_args.g_i[model.species[reactant].name]; - } - - - } - } - } - } - - return tau_args; -} - -double select(Gillespy::Model &model, TauArgs &tau_args, const double &tau_tol, const double ¤t_time, const double &save_time, const std::vector &propensity_values, const std::vector ¤t_state){ - double tau; //tau time to step; - std::map critical_taus; //Mapping of possible critical_taus, to be evaluated - std::map mu_i; - std::map sigma_i; - bool critical = false; // system-wide flag, true when any reaction is critical - double non_critical_tau = 0; // holds the smallest tau time for non-critical reactions - double critical_tau = 0; // holds the smallest tau time for critical reactions - int v;//used for number of population reactant consumes - // initialize mu_i and sigma_i to 0 - for(int spec = 0; spec 0){ - critical = true; // Critical reaction present in simulation - } - int consumed = abs(model.reactions[reaction].species_change[reactant]); - mu_i[model.species[reactant].name] += consumed*propensity_values[reaction];//Cao, Gillespie, Petzold 32a - sigma_i[model.species[reactant].name] += std::pow(consumed,2) * propensity_values[reaction];//Cao, Gillespie, Petzold 32a - } - } - } - - // If a critical reaction is present, estimate tau for a single firing of each - // critical reaction with propensity > 0, and take the smallest tau - if (critical == true){ - for (int reaction = 0; reaction < model.number_reactions; reaction++){ - if (propensity_values[reaction]>0) - critical_taus[model.reactions[reaction].name] = 1/propensity_values[reaction]; - } - std::pair min; - //find min of critical_taus - min = *min_element(critical_taus.begin(), critical_taus.end(),[](const auto& lhs, const auto& rhs){ return lhs.second < rhs.second;}); - critical_tau = min.second; - } - - if (tau_args.g_i_lambdas.size()>0){ - for (auto const& x : tau_args.g_i_lambdas) - { - tau_args.g_i[x.first] = tau_args.g_i_lambdas[x.first](tau_args.g_i[x.first]); - tau_args.epsilon_i[x.first] = tau_tol / tau_args.g_i[x.first]; - tau_args.g_i_lambdas.erase(x.first);//MAYBE SHOULDN'T ERASE, may break loop/may be understanding this part wrong - } - } - std::map tau_i; //Mapping of possible non-critical_taus, to be evaluated - for (const auto &r : tau_args.reactants) - { - double calculated_max = tau_args.epsilon_i[r.name] * current_state[r.id]; - double max_pop_change_mean = std::max(calculated_max, 1.0); - double max_pop_change_sd = pow(max_pop_change_mean,2); - if (mu_i[r.name] > 0){ // Cao, Gillespie, Petzold 33 - tau_i[r.name] = std::min(std::abs(max_pop_change_mean / mu_i[r.name]), max_pop_change_sd / sigma_i[r.name]); - } - } - - if (tau_i.size()>0){ - std::pair min; - //find min of tau_i - min = *min_element(tau_i.begin(), tau_i.end(),[](const auto& lhs, const auto& rhs){ return lhs.second < rhs.second;}); - non_critical_tau = min.second; - } - - // If all reactions are non-critical, use non-critical tau. - if (critical == false){ - tau = non_critical_tau; - } - // If all reactions are critical, use critical tau. - else if (tau_i.size()==0){ - tau = critical_tau; - } - // If there are both critical, and non critical reactions, - // Take the shortest tau between critica and non-critical. - else{ - tau = std::min(non_critical_tau, critical_tau); - } - // If selected tau exceeds save time, integrate to save time - if (tau > 0){ - tau = std::max(tau, 1e-10); - if (save_time - current_time > 0){ - tau = std::min(tau, save_time - current_time); - } - } - else{ - tau = save_time - current_time; - } - - return tau; -} - std::pair,double> get_reactions(const Gillespy::Model *model, const std::vector &propensity_values, double tau_step, double current_time, double save_time){ /* * Helper Function to get reactions fired from t to t+tau. Effects two values: diff --git a/gillespy2/solvers/cpp/c_base/tau_leaping_cpp_solver/tau_leaper.h b/gillespy2/solvers/cpp/c_base/tau_leaping_cpp_solver/tau_leaper.h index f665cee63..cfbf0d514 100644 --- a/gillespy2/solvers/cpp/c_base/tau_leaping_cpp_solver/tau_leaper.h +++ b/gillespy2/solvers/cpp/c_base/tau_leaping_cpp_solver/tau_leaper.h @@ -3,7 +3,7 @@ #include "model.h" namespace Gillespy{ - void tau_leaper(Gillespy::Simulation* simulation, const double tau_tol); + void tau_leaper(Simulation* simulation, const double tau_tol); } #endif // TAU_LEAPER_H From 7678c9b6ee911e0dca562c0328e93d772adf1051 Mon Sep 17 00:00:00 2001 From: mdip226 Date: Mon, 15 Mar 2021 12:05:43 -0400 Subject: [PATCH 05/88] try to compile --- .../solvers/cpp/c_base/tau_leaping_cpp_solver/makefile | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/gillespy2/solvers/cpp/c_base/tau_leaping_cpp_solver/makefile b/gillespy2/solvers/cpp/c_base/tau_leaping_cpp_solver/makefile index 0b874b177..7d8e3d610 100644 --- a/gillespy2/solvers/cpp/c_base/tau_leaping_cpp_solver/makefile +++ b/gillespy2/solvers/cpp/c_base/tau_leaping_cpp_solver/makefile @@ -7,12 +7,12 @@ OBJ = tau_leaper.o model.o tau.o model.o: $(CC) -c -o model.o $(CBASE_DIR)/model.cpp $(CFLAGS) -I$(CBASE_DIR) -tau_leaper.o: - $(CC) -c -o tau_leaper.o $(GILLESPY_CPP_TAU_DIR)/tau_leaper.cpp $(CFLAGS) -I$(CBASE_DIR) -I$(GILLESPY_CPP_TAU_DIR) - tau.o: $(CC) -c -o tau.o $(CBASE_DIR)/Tau/tau.cpp $(CFLAGS) -I$(CBASE_DIR) +tau_leaper.o: + $(CC) -c -o tau_leaper.o $(GILLESPY_CPP_TAU_DIR)/tau_leaper.cpp $(CFLAGS) -I$(CBASE_DIR) -I$(GILLESPY_CPP_TAU_DIR) + TauSimulation.o: $(CC) -c -o TauSimulation.o TauSimulation.cpp $(CFLAGS) -I$(CBASE_DIR) -I$(GILLESPY_CPP_TAU_DIR) From 821b6e9d43be78bc5d7c2ce2bfc772c80353a427 Mon Sep 17 00:00:00 2001 From: Matthew Dippel Date: Mon, 15 Mar 2021 13:18:14 -0400 Subject: [PATCH 06/88] tau.h --- .../cpp/c_base/tau_hybrid_cpp_solver/Tau.cpp | 246 ------------------ .../c_base/tau_leaping_cpp_solver/makefile | 2 +- 2 files changed, 1 insertion(+), 247 deletions(-) delete mode 100644 gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/Tau.cpp diff --git a/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/Tau.cpp b/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/Tau.cpp deleted file mode 100644 index 6281eea14..000000000 --- a/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/Tau.cpp +++ /dev/null @@ -1,246 +0,0 @@ -#include "tau_leaper.h" -#include //Included for mt19937 random number generator, poisson distribution -#include //Included for natural logarithm -#include //Included for timeout signal handling -#include // For testing output -#include -#include -#include -#include -#include -#include -#include - -namespace Gillespy -{ - bool interrupted = false; - - void signalHandler(int signum) - { - interrupted = true; - } - struct TauArgs - { - //Highest Order Reaction - std::map HOR; - std::set reactants; - //Below are g_i_lambdas, pop element when used - std::map> g_i_lambdas; - std::map g_i; - std::map epsilon_i; - std::map> reactions_reactants; - std::map> products; - int critical_threshold = 10; - }; - - TauArgs initialize(Gillespy::Model &model, double tau_tol) - { - - // Initialize TauArgs struct to be returned as a pointer - TauArgs tau_args; - // Initialize highest order rxns to 0 - for (int i = 0; i < model.number_species; i++) - { - tau_args.HOR[model.species[i].name] = 0; - } - - for (int r = 0; r < model.number_reactions; r++) - { - int rxn_order = 0; - for (int spec = 0; spec < model.number_species; spec++) - { - if (model.reactions[r].species_change[spec] > 0) - tau_args.products[r].push_back(spec); - else if (model.reactions[r].species_change[spec] < 0) - { - rxn_order += 1; - tau_args.reactions_reactants[r].push_back(spec); - tau_args.reactants.insert(model.species[spec]); - } - } - - // if this reaction's order is higher than previous, set - if (tau_args.reactions_reactants[r].size() > 0) - { - for (auto const &reactant : tau_args.reactions_reactants[r]) - { - if (rxn_order > tau_args.HOR[model.species[reactant].name]) - { - tau_args.HOR[model.species[reactant].name] = rxn_order; - tau_args.g_i[model.species[reactant].name] = tau_args.HOR[model.species[reactant].name]; - - int count = std::abs(model.reactions[r].species_change[reactant]); - if (count == 2 && rxn_order == 2) - { - auto lambda = [](double x) { return (2 + (1 / (x - 1))); }; - tau_args.g_i_lambdas[model.species[reactant].name] = lambda; - } - else if (count == 2 && rxn_order == 3) - { - auto lambda = [](double x) { return ((3 / 2) * (2 + (1 / (x - 1)))); }; - tau_args.g_i_lambdas[model.species[reactant].name] = lambda; - } - else if (count == 3) - { - auto lambda = [](double x) { return (3 + (1 / (x - 1)) + (2 / (x - 2))); }; - tau_args.g_i_lambdas[model.species[reactant].name] = lambda; - } - else - { - tau_args.g_i[model.species[reactant].name] = tau_args.HOR[model.species[reactant].name]; - tau_args.epsilon_i[model.species[reactant].name] = tau_tol / tau_args.g_i[model.species[reactant].name]; - } - } - } - } - } - - return tau_args; - } - - double select(Gillespy::Model &model, TauArgs &tau_args, const double &tau_tol, const double ¤t_time, const double &save_time, const std::vector &propensity_values, const std::vector ¤t_state) - { - double tau; //tau time to step; - std::map critical_taus; //Mapping of possible critical_taus, to be evaluated - std::map mu_i; - std::map sigma_i; - bool critical = false; // system-wide flag, true when any reaction is critical - double non_critical_tau = 0; // holds the smallest tau time for non-critical reactions - double critical_tau = 0; // holds the smallest tau time for critical reactions - - int v; //used for number of population reactant consumes - - // initialize mu_i and sigma_i to 0 - for (int spec = 0; spec < model.number_species; spec++) - { - mu_i[model.species[spec].name] = 0; - sigma_i[model.species[spec].name] = 0; - } - // Determine if there are any critical reactions, update mu_i and sigma_i - for (int reaction = 0; reaction < model.number_reactions; reaction++) - { - for (auto const &reactant : tau_args.reactions_reactants[reaction]) - { - if (model.reactions[reaction].species_change[reactant] < 0) - { - v = abs(model.reactions[reaction].species_change[reactant]); - - if ((double)current_state[reactant] / v < tau_args.critical_threshold && propensity_values[reaction] > 0) - { - critical = true; // Critical reaction present in simulation - } - int consumed = abs(model.reactions[reaction].species_change[reactant]); - mu_i[model.species[reactant].name] += consumed * propensity_values[reaction]; //Cao, Gillespie, Petzold 32a - sigma_i[model.species[reactant].name] += std::pow(consumed, 2) * propensity_values[reaction]; //Cao, Gillespie, Petzold 32a - } - } - } - - // If a critical reaction is present, estimate tau for a single firing of each - // critical reaction with propensity > 0, and take the smallest tau - if (critical == true) - { - for (int reaction = 0; reaction < model.number_reactions; reaction++) - { - if (propensity_values[reaction] > 0) - critical_taus[model.reactions[reaction].name] = 1 / propensity_values[reaction]; - } - std::pair min; - //find min of critical_taus - min = *min_element(critical_taus.begin(), critical_taus.end(), [](const auto &lhs, const auto &rhs) { return lhs.second < rhs.second; }); - critical_tau = min.second; - } - - if (tau_args.g_i_lambdas.size() > 0) - { - for (auto const &x : tau_args.g_i_lambdas) - { - tau_args.g_i[x.first] = tau_args.g_i_lambdas[x.first](tau_args.g_i[x.first]); - tau_args.epsilon_i[x.first] = tau_tol / tau_args.g_i[x.first]; - tau_args.g_i_lambdas.erase(x.first); //MAYBE SHOULDN'T ERASE, may break loop/may be understanding this part wrong - } - } - - std::map tau_i; //Mapping of possible non-critical_taus, to be evaluated - - for (const auto &r : tau_args.reactants) - { - double calculated_max = tau_args.epsilon_i[r.name] * current_state[r.id]; - double max_pop_change_mean = std::max(calculated_max, 1.0); - double max_pop_change_sd = pow(max_pop_change_mean, 2); - if (mu_i[r.name] > 0) - { // Cao, Gillespie, Petzold 33 - tau_i[r.name] = std::min(std::abs(max_pop_change_mean / mu_i[r.name]), max_pop_change_sd / sigma_i[r.name]); - } - } - - if (tau_i.size() > 0) - { - std::pair min; - //find min of tau_i - min = *min_element(tau_i.begin(), tau_i.end(), [](const auto &lhs, const auto &rhs) { return lhs.second < rhs.second; }); - non_critical_tau = min.second; - } - - // If all reactions are non-critical, use non-critical tau. - if (critical == false) - { - tau = non_critical_tau; - } - // If all reactions are critical, use critical tau. - else if (tau_i.size() == 0) - { - tau = critical_tau; - } - // If there are both critical, and non critical reactions, - // Take the shortest tau between critica and non-critical. - else - { - tau = std::min(non_critical_tau, critical_tau); - } - // If selected tau exceeds save time, integrate to save time - if (tau > 0) - { - tau = std::max(tau, 1e-10); - if (save_time - current_time > 0) - { - tau = std::min(tau, save_time - current_time); - } - } - else - { - tau = save_time - current_time; - } - - return tau; - } - - std::pair, double> get_reactions(const Gillespy::Model *model, const std::vector &propensity_values, double tau_step, double current_time, double save_time) - { - /* - * Helper Function to get reactions fired from t to t+tau. Effects two values: - *rxn_count - dict with key=Reaction channel value=number of times fired - *curr_time - float representing current time - */ - - if (current_time + tau_step > save_time) - tau_step = save_time - current_time; - - std::map rxn_count; // map of how many times reaction is fired - std::random_device rd; - std::mt19937 generator(rd()); - std::pair, double> values; // value pair to be returned, map of times {map of times reaction fired, current time} - - for (int i = 0; i < model->number_reactions; i++) - { - std::poisson_distribution poisson(propensity_values[i] * tau_step); - rxn_count[model->reactions[i].name] = poisson(generator); - } - current_time = current_time + tau_step; - values.first = rxn_count; - values.second = current_time; - return values; - } - - -} diff --git a/gillespy2/solvers/cpp/c_base/tau_leaping_cpp_solver/makefile b/gillespy2/solvers/cpp/c_base/tau_leaping_cpp_solver/makefile index 0b874b177..a5a66d575 100644 --- a/gillespy2/solvers/cpp/c_base/tau_leaping_cpp_solver/makefile +++ b/gillespy2/solvers/cpp/c_base/tau_leaping_cpp_solver/makefile @@ -8,7 +8,7 @@ model.o: $(CC) -c -o model.o $(CBASE_DIR)/model.cpp $(CFLAGS) -I$(CBASE_DIR) tau_leaper.o: - $(CC) -c -o tau_leaper.o $(GILLESPY_CPP_TAU_DIR)/tau_leaper.cpp $(CFLAGS) -I$(CBASE_DIR) -I$(GILLESPY_CPP_TAU_DIR) + $(CC) -c -o tau_leaper.o $(GILLESPY_CPP_TAU_DIR)/tau_leaper.cpp $(CFLAGS) -I$(CBASE_DIR) -I$(GILLESPY_CPP_TAU_DIR) -I$(CBASE_DIR)/Tau tau.o: $(CC) -c -o tau.o $(CBASE_DIR)/Tau/tau.cpp $(CFLAGS) -I$(CBASE_DIR) From 397388a280bf8806863136fb19a762b29d9995ae Mon Sep 17 00:00:00 2001 From: Matthew Dippel Date: Mon, 22 Mar 2021 13:55:30 -0400 Subject: [PATCH 07/88] settings --- gillespy2/solvers/cpp/c_base/model.h | 14 +++++ .../TauHybridCSolver.cpp | 52 ++++++++++++++++++- .../cpp/c_base/tau_hybrid_cpp_solver/makefile | 7 ++- .../tau_leaping_cpp_solver/tau_leaper.cpp | 5 -- 4 files changed, 70 insertions(+), 8 deletions(-) diff --git a/gillespy2/solvers/cpp/c_base/model.h b/gillespy2/solvers/cpp/c_base/model.h index a7cda5334..80b606cb0 100644 --- a/gillespy2/solvers/cpp/c_base/model.h +++ b/gillespy2/solvers/cpp/c_base/model.h @@ -5,6 +5,7 @@ #include #include #include +#include "sundials_types.h" namespace Gillespy{ @@ -72,5 +73,18 @@ namespace Gillespy{ void simulationODEINIT(Model* model, Simulation &simulation); void simulationSSAINIT(Model* model, Simulation &simulation); + typedef struct { + // CVODE constants returned if bad output, or success output. + // Constants: CV_SUCCESS, + // CV_MEM_NULL: CVODE memory block not initialized through call to CVodeCreate + // CV_NO_MALLOC: The allocation function CVodeInit not called + // CV_ILL_Input: An input tolerance was negative + int flag; + // absolute tolerace of a system + realtype abstol; + // relative tolerance of system + realtype reltol; + // double max_step; + } integrator_options; } #endif diff --git a/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/TauHybridCSolver.cpp b/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/TauHybridCSolver.cpp index e73264962..e58c11bfd 100644 --- a/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/TauHybridCSolver.cpp +++ b/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/TauHybridCSolver.cpp @@ -1,4 +1,6 @@ #include +#include //Included for timeout signal handling +#include #include "cvode.h" // prototypes for CVODE fcts., consts. #include "nvector_serial.h" // access to serial N_Vector #include "sunlinsol_spgmr.h" //access to SPGMR SUNLinearSolver @@ -7,6 +9,7 @@ #include "sundials_math.h" // contains the macros ABS, SUNSQR, EXP #include "TauHybridCSolver.h" #include "model.h" +#include "tau.h" using namespace Gillespy; #define NV_Ith_S(v,i) (NV_DATA_S(v)[i]) // Access to individual components of data array, of N len vector @@ -16,11 +19,56 @@ static int f(realtype t, N_Vector y, N_Vector y_dot, void *user_data); // forwar struct UserData { Gillespy::Simulation *my_sim; }; +namespace Gillespy { + bool interrupted = false; -void TauHybridCSolver(Gillespy::Simulation* simulation, double increment){ + void signalHandler(int signum) + { + interrupted = true; + } + std::pair, double> get_reactions(const Gillespy::Model *model, const std::vector &propensity_values, double tau_step, double current_time, double save_time) + { + /* + * Helper Function to get reactions fired from t to t+tau. Effects two values: + *rxn_count - dict with key=Reaction channel value=number of times fired + *curr_time - float representing current time + */ + + if (current_time + tau_step > save_time) + tau_step = save_time - current_time; + + std::map rxn_count; // map of how many times reaction is fired + std::random_device rd; + std::mt19937 generator(rd()); + std::pair, double> values; // value pair to be returned, map of times {map of times reaction fired, current time} + + for (int i = 0; i < model->number_reactions; i++) + { + std::poisson_distribution poisson(propensity_values[i] * tau_step); + rxn_count[model->reactions[i].name] = poisson(generator); + } + current_time = current_time + tau_step; + values.first = rxn_count; + values.second = current_time; + return values; + } -} + struct set_recommended_ODE_defaults{ + //?????? + + } + + + void TauHybridCSolver(Gillespy::Simulation *simulation, double increment) + { + signal(SIGINT, signalHandler); + + if (simulation) { + + } + } +} static int f(realtype t, N_Vector y, N_Vector y_dot, void *user_data) { // N_VGetArrayPointer returns a pointer to the data in the N_Vector class. diff --git a/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/makefile b/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/makefile index 480a4574e..e879c49eb 100644 --- a/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/makefile +++ b/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/makefile @@ -1,3 +1,6 @@ +# SUNDIALS_DIR = ../Sundials +# CBASE_DIR = ../ + CC=g++ CFLAGS = -c -std=c++14 -Wall -O3 LINKFLAGS = -L. -std=c++14 -Wall -O3 @@ -15,8 +18,10 @@ SUNOBJ = cvode_nls.o cvode_io.o sundials_iterative.o cvode_proj.o sundials_matri sundials_linearsolver.o sundials_nonlinearsolver.o sundials_nvector_senswrapper.o sunnonlinsol_newton.o \ sundials_nvector.o nvector_serial.o cvode.o cvode_spils.o sundials_math.o sunlinsol_spgmr.o +# all: model.o TauHybridSimulation.o TauHybridCSolver.o TauHybridSimulation + model.o: - $(CC) -c -o model.o $(CBASE_DIR)/model.cpp $(CFLAGS) -I$(CBASE_DIR) + $(CC) -c -o model.o $(CBASE_DIR)/model.cpp $(CFLAGS) -I$(CBASE_DIR) -I$(INCDIR) %.o: $(SRCDIR)/%.c $(CC) -c -o $@ $< $(CFLAGS) -I$(INCDIR) diff --git a/gillespy2/solvers/cpp/c_base/tau_leaping_cpp_solver/tau_leaper.cpp b/gillespy2/solvers/cpp/c_base/tau_leaping_cpp_solver/tau_leaper.cpp index 86019b3d5..0780c9dc8 100644 --- a/gillespy2/solvers/cpp/c_base/tau_leaping_cpp_solver/tau_leaper.cpp +++ b/gillespy2/solvers/cpp/c_base/tau_leaping_cpp_solver/tau_leaper.cpp @@ -19,11 +19,6 @@ void signalHandler(int signum){ interrupted = true; } - - - - - std::pair,double> get_reactions(const Gillespy::Model *model, const std::vector &propensity_values, double tau_step, double current_time, double save_time){ /* * Helper Function to get reactions fired from t to t+tau. Effects two values: From 2b7b4a5948c8165a1f458742bd787ef9132d2a94 Mon Sep 17 00:00:00 2001 From: Matthew Dippel Date: Sun, 28 Mar 2021 20:08:37 -0400 Subject: [PATCH 08/88] compilation things --- .../TauHybridCSolver.cpp | 38 +++++++++++++++---- 1 file changed, 30 insertions(+), 8 deletions(-) diff --git a/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/TauHybridCSolver.cpp b/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/TauHybridCSolver.cpp index e58c11bfd..911a7f8db 100644 --- a/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/TauHybridCSolver.cpp +++ b/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/TauHybridCSolver.cpp @@ -12,13 +12,26 @@ #include "tau.h" using namespace Gillespy; -#define NV_Ith_S(v,i) (NV_DATA_S(v)[i]) // Access to individual components of data array, of N len vector +// #define NV_Ith_S(v,i) (NV_DATA_S(v)[i]) // Access to individual components of data array, of N len vector static int f(realtype t, N_Vector y, N_Vector y_dot, void *user_data); // forward declare function to be used to solve RHS of ODE struct UserData { Gillespy::Simulation *my_sim; }; +struct IntegratorOptions{ + // CVODE constants returned if bad output, or success output. + // Constants: CV_SUCCESS, + // CV_MEM_NULL: CVODE memory block not initialized through call to CVodeCreate + // CV_NO_MALLOC: The allocation function CVodeInit not called + // CV_ILL_Input: An input tolerance was negative + int flag; + // absolute tolerace of a system + realtype abstol; + // relative tolerance of system + realtype reltol; + // double max_step; +}; namespace Gillespy { bool interrupted = false; @@ -53,19 +66,28 @@ namespace Gillespy { return values; } - struct set_recommended_ODE_defaults{ - //?????? - - } - void TauHybridCSolver(Gillespy::Simulation *simulation, double increment) + void TauHybridCSolver(Gillespy::Simulation *simulation, const double tau_tol) { signal(SIGINT, signalHandler); - if (simulation) { - + uint32_t num_species = (simulation->model)->number_species; + uint32_t num_reactions = (simulation->model)->number_reactions; + Model &model = *(simulation->model); + TauArgs tau_args = initialize(*(simulation->model),tau_tol); + double increment = simulation->timeline[1] - simulation->timeline[0]; + + //initialize current_state vector to 0 for each species + std::vector current_state(num_species); + //initialize propensity_values to 0 for each species + std::vector propensity_values(num_reactions); + + //copy initial state for each trajectory + for(uint32_t s = 0; s < num_species; s++){ + simulation->trajectories[0][0][s] = model.species[s].initial_population; + } } } } From 614cafc2972eb40c242e822efe4f6ccc6a61ec85 Mon Sep 17 00:00:00 2001 From: Matthew Dippel Date: Sun, 28 Mar 2021 20:08:51 -0400 Subject: [PATCH 09/88] compilation things --- gillespy2/solvers/cpp/c_base/model.h | 16 ++-------------- .../cpp/c_base/tau_hybrid_cpp_solver/makefile | 2 +- gillespy2/solvers/cpp/tau_hybrid_c_solver.py | 2 +- 3 files changed, 4 insertions(+), 16 deletions(-) diff --git a/gillespy2/solvers/cpp/c_base/model.h b/gillespy2/solvers/cpp/c_base/model.h index 80b606cb0..8235810e1 100644 --- a/gillespy2/solvers/cpp/c_base/model.h +++ b/gillespy2/solvers/cpp/c_base/model.h @@ -5,7 +5,7 @@ #include #include #include -#include "sundials_types.h" +// #include "sundials_types.h" namespace Gillespy{ @@ -73,18 +73,6 @@ namespace Gillespy{ void simulationODEINIT(Model* model, Simulation &simulation); void simulationSSAINIT(Model* model, Simulation &simulation); - typedef struct { - // CVODE constants returned if bad output, or success output. - // Constants: CV_SUCCESS, - // CV_MEM_NULL: CVODE memory block not initialized through call to CVodeCreate - // CV_NO_MALLOC: The allocation function CVodeInit not called - // CV_ILL_Input: An input tolerance was negative - int flag; - // absolute tolerace of a system - realtype abstol; - // relative tolerance of system - realtype reltol; - // double max_step; - } integrator_options; + } #endif diff --git a/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/makefile b/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/makefile index e879c49eb..07f93792b 100644 --- a/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/makefile +++ b/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/makefile @@ -30,7 +30,7 @@ TauHybridSimulation.o: $(CC) -c -o TauHybridSimulation.o TauHybridSimulation.cpp $(CFLAGS) -I$(CBASE_DIR) -I$(GILLESPY_CPP_TAU_HYBRID_DIR) TauHybridCSolver.o: - $(CC) -c -o TauHybridCSolver.o $(GILLESPY_CPP_TAU_HYBRID_DIR)/TauHybridCSolver.cpp $(CFLAGS) -I$(CBASE_DIR) -I$(SRCDIR) -I$(INCDIR) + $(CC) -c -o TauHybridCSolver.o $(GILLESPY_CPP_TAU_HYBRID_DIR)/TauHybridCSolver.cpp $(CFLAGS) -I$(CBASE_DIR) -I$(SRCDIR) -I$(INCDIR) -I$(CBASE_DIR)/Tau TauHybridSimulation: TauHybridCSolver.o TauHybridSimulation.o $(SUNOBJ) model.o $(CC) -o TauHybridSimulation TauHybridCSolver.o TauHybridSimulation.o $(SUNOBJ) model.o $(LINKFLAGS) diff --git a/gillespy2/solvers/cpp/tau_hybrid_c_solver.py b/gillespy2/solvers/cpp/tau_hybrid_c_solver.py index 1a49d0726..6f77c09a3 100644 --- a/gillespy2/solvers/cpp/tau_hybrid_c_solver.py +++ b/gillespy2/solvers/cpp/tau_hybrid_c_solver.py @@ -67,7 +67,7 @@ def __write_template(self): if self.variable: template_file = "VariableTauHybridTemplate.cpp" else: - template_file = "TauhybridTemplate.cpp" + template_file = "TauHybridTemplate.cpp" with open(os.path.join(GILLESPY_CPP_TAU_HYBRID_DIR, template_file), 'r') as template: # Write simulation C++ file. From d041f760978d657a20848b40fc92df31f1934e70 Mon Sep 17 00:00:00 2001 From: Matthew Dippel Date: Sun, 28 Mar 2021 22:28:15 -0400 Subject: [PATCH 10/88] checking type of simulation --- gillespy2/solvers/cpp/c_base/model.cpp | 46 ++++++++++++------- gillespy2/solvers/cpp/c_base/model.h | 14 ++++-- .../cpp/c_base/ode_cpp_solver/ODETemplate.cpp | 2 +- .../ode_cpp_solver/VariableODETemplate.cpp | 2 +- .../ssa_cpp_solver/SimulationTemplate.cpp | 1 + .../VariableSimulationTemplate.cpp | 1 + .../TauHybridCSolver.cpp | 2 + .../TauHybridTemplate.cpp | 4 +- .../VariableTauHybridTemplate.cpp | 2 +- .../TauSimulationTemplate.cpp | 1 + .../VariableTauSimulationTemplate.cpp | 1 + 11 files changed, 51 insertions(+), 25 deletions(-) diff --git a/gillespy2/solvers/cpp/c_base/model.cpp b/gillespy2/solvers/cpp/c_base/model.cpp index fad3b6a09..38e9a4b41 100644 --- a/gillespy2/solvers/cpp/c_base/model.cpp +++ b/gillespy2/solvers/cpp/c_base/model.cpp @@ -2,6 +2,7 @@ namespace Gillespy{ + Model :: Model(std :: vector species_names, std :: vector species_populations, std :: vector reaction_names): number_species(species_names.size()), number_reactions(reaction_names.size()) @@ -57,15 +58,13 @@ namespace Gillespy{ } } - +// initialize timeline, void simulationODEINIT(Model* model, Simulation &simulation){ simulation.timeline = new double[simulation.number_timesteps]; double timestep_size = simulation.end_time/(simulation.number_timesteps-1); for(unsigned int i = 0; i < simulation.number_timesteps; i++){ simulation.timeline[i] = timestep_size * i; } - - unsigned int trajectory_size = simulation.number_timesteps * (model -> number_species); simulation.trajectories_1DODE = new double[simulation.number_trajectories * trajectory_size]; simulation.trajectoriesODE = new double**[simulation.number_trajectories]; @@ -77,22 +76,35 @@ void simulationODEINIT(Model* model, Simulation &simulation){ } } +void simulationINIT(Model* model, Simulation &sim) { + int type = sim.type; + if (type == ODE || type == HYBRID) { + simulationODEINIT(model, sim); + } + else if (type == SSA || type == TAU || type == HYBRID) + { + simulationSSAINIT(model, sim); + } + +} + Simulation :: ~Simulation(){ + int type = this->type; delete timeline; - if (ISODE==1){ - delete trajectories_1DODE; - for(unsigned int i = 0; i < number_trajectories; i++){ - delete trajectoriesODE[i]; - } - delete trajectoriesODE; - }else{ - - delete trajectories_1D; - for(unsigned int i = 0; i < number_trajectories; i++){ - delete trajectories[i]; + if (type == ODE || type == HYBRID){ + delete trajectories_1DODE; + for(unsigned int i = 0; i < number_trajectories; i++){ + delete trajectoriesODE[i]; + } + delete trajectoriesODE; } - delete trajectories; + else if (type == SSA || type == TAU || type == HYBRID){ + delete trajectories_1D; + for(unsigned int i = 0; i < number_trajectories; i++){ + delete trajectories[i]; + } + delete trajectories; } } @@ -102,7 +114,7 @@ void simulationODEINIT(Model* model, Simulation &simulation){ os << simulation.timeline[i] << " "; for(unsigned int trajectory = 0; trajectory < simulation.number_trajectories; trajectory++){ for(unsigned int j = 0; j < simulation.model -> number_species; j++){ - if (simulation.ISODE==1){os << simulation.trajectoriesODE[trajectory][i][j] << " ";} + if (simulation.type==ODE){os << simulation.trajectoriesODE[trajectory][i][j] << " ";} else{os << simulation.trajectories[trajectory][i][j] << " ";} } } @@ -116,7 +128,7 @@ void Simulation :: output_results_buffer(std::ostream& os){ for (int j = 0; jnumber_species; k++){ - if (ISODE==1){os< #include #include -// #include "sundials_types.h" namespace Gillespy{ @@ -44,14 +43,20 @@ namespace Gillespy{ virtual ~IPropensityFunction() {}; }; - + #define SSA 1 + #define ODE 2 + #define TAU 3 + #define HYBRID 4 struct Simulation{ Model* model; ~Simulation(); - int ISODE = 0; // if 0, not ODE sim, if 1, ODE sim + // the type of simulation - SSA, ODE, TAU, or HYBRID + int type; + // array representing discrete time steps for the simulation double* timeline; + // double end_time; double current_time; int random_seed; @@ -60,6 +65,9 @@ namespace Gillespy{ unsigned int number_trajectories; unsigned int* trajectories_1D; + // first dimension: trajectory by number + // second dimension: the associated timesteps for that trajectory + // third dimension: the unsigned int*** trajectories; double* trajectories_1DODE; diff --git a/gillespy2/solvers/cpp/c_base/ode_cpp_solver/ODETemplate.cpp b/gillespy2/solvers/cpp/c_base/ode_cpp_solver/ODETemplate.cpp index cfd28ce20..e2507e39e 100644 --- a/gillespy2/solvers/cpp/c_base/ode_cpp_solver/ODETemplate.cpp +++ b/gillespy2/solvers/cpp/c_base/ode_cpp_solver/ODETemplate.cpp @@ -83,7 +83,7 @@ __DEFINE_REACTIONS_ Simulation simulation; Model* modelptr; modelptr = &model; - simulation.ISODE=1; + simulation.type = ODE; simulation.model = modelptr; simulation.end_time = end_time; simulation.random_seed = random_seed; diff --git a/gillespy2/solvers/cpp/c_base/ode_cpp_solver/VariableODETemplate.cpp b/gillespy2/solvers/cpp/c_base/ode_cpp_solver/VariableODETemplate.cpp index 3a7e467b0..4307f7c4e 100644 --- a/gillespy2/solvers/cpp/c_base/ode_cpp_solver/VariableODETemplate.cpp +++ b/gillespy2/solvers/cpp/c_base/ode_cpp_solver/VariableODETemplate.cpp @@ -95,7 +95,7 @@ __DEFINE_REACTIONS_ Simulation simulation; Model* modelptr; modelptr = &model; - simulation.ISODE=1; + simulation.type = ODE; simulation.model = modelptr; simulation.end_time = end_time; simulation.random_seed = random_seed; diff --git a/gillespy2/solvers/cpp/c_base/ssa_cpp_solver/SimulationTemplate.cpp b/gillespy2/solvers/cpp/c_base/ssa_cpp_solver/SimulationTemplate.cpp index c2cd8e8f2..3ca0697cb 100644 --- a/gillespy2/solvers/cpp/c_base/ssa_cpp_solver/SimulationTemplate.cpp +++ b/gillespy2/solvers/cpp/c_base/ssa_cpp_solver/SimulationTemplate.cpp @@ -77,6 +77,7 @@ __DEFINE_REACTIONS_ Simulation simulation; Model* modelptr; modelptr = &model; + simulation.type = SSA; simulation.model = modelptr; simulation.end_time = end_time; simulation.random_seed = random_seed; diff --git a/gillespy2/solvers/cpp/c_base/ssa_cpp_solver/VariableSimulationTemplate.cpp b/gillespy2/solvers/cpp/c_base/ssa_cpp_solver/VariableSimulationTemplate.cpp index 9d6e01d26..478dd429b 100644 --- a/gillespy2/solvers/cpp/c_base/ssa_cpp_solver/VariableSimulationTemplate.cpp +++ b/gillespy2/solvers/cpp/c_base/ssa_cpp_solver/VariableSimulationTemplate.cpp @@ -85,6 +85,7 @@ __DEFINE_REACTIONS_ Simulation simulation; Model* modelptr; modelptr = &model; + simulation.type = SSA; simulation.model = modelptr; simulation.end_time = end_time; simulation.random_seed = random_seed; diff --git a/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/TauHybridCSolver.cpp b/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/TauHybridCSolver.cpp index 911a7f8db..18693830c 100644 --- a/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/TauHybridCSolver.cpp +++ b/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/TauHybridCSolver.cpp @@ -88,6 +88,8 @@ namespace Gillespy { for(uint32_t s = 0; s < num_species; s++){ simulation->trajectories[0][0][s] = model.species[s].initial_population; } + + //Simulate for each trajectory } } } diff --git a/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/TauHybridTemplate.cpp b/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/TauHybridTemplate.cpp index 80be27ffe..6e2ddbda5 100644 --- a/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/TauHybridTemplate.cpp +++ b/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/TauHybridTemplate.cpp @@ -83,7 +83,7 @@ __DEFINE_REACTIONS_ Simulation simulation; Model* modelptr; modelptr = &model; - simulation.ISODE=1; + simulation.type = HYBRID; simulation.model = modelptr; simulation.end_time = end_time; simulation.random_seed = random_seed; @@ -92,7 +92,7 @@ __DEFINE_REACTIONS_ simulation.propensity_function = propFun; simulationODEINIT(&model, simulation); // Perform ODE // - ODESolver(&simulation,increment); + // TauHybridCSolver(&simulation,); simulation.output_results_buffer(std :: cout); delete propFun; return 0; diff --git a/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/VariableTauHybridTemplate.cpp b/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/VariableTauHybridTemplate.cpp index 3a7e467b0..022787323 100644 --- a/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/VariableTauHybridTemplate.cpp +++ b/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/VariableTauHybridTemplate.cpp @@ -95,7 +95,7 @@ __DEFINE_REACTIONS_ Simulation simulation; Model* modelptr; modelptr = &model; - simulation.ISODE=1; + simulation.type = HYBRID; simulation.model = modelptr; simulation.end_time = end_time; simulation.random_seed = random_seed; diff --git a/gillespy2/solvers/cpp/c_base/tau_leaping_cpp_solver/TauSimulationTemplate.cpp b/gillespy2/solvers/cpp/c_base/tau_leaping_cpp_solver/TauSimulationTemplate.cpp index 4ed5437a7..9aa5f1a7b 100644 --- a/gillespy2/solvers/cpp/c_base/tau_leaping_cpp_solver/TauSimulationTemplate.cpp +++ b/gillespy2/solvers/cpp/c_base/tau_leaping_cpp_solver/TauSimulationTemplate.cpp @@ -84,6 +84,7 @@ __DEFINE_REACTIONS_ Simulation simulation; Model* modelptr; modelptr = &model; + simulation.type = TAU; simulation.model = modelptr; simulation.end_time = end_time; simulation.random_seed = random_seed; diff --git a/gillespy2/solvers/cpp/c_base/tau_leaping_cpp_solver/VariableTauSimulationTemplate.cpp b/gillespy2/solvers/cpp/c_base/tau_leaping_cpp_solver/VariableTauSimulationTemplate.cpp index 02cdc6be2..c77fd7559 100644 --- a/gillespy2/solvers/cpp/c_base/tau_leaping_cpp_solver/VariableTauSimulationTemplate.cpp +++ b/gillespy2/solvers/cpp/c_base/tau_leaping_cpp_solver/VariableTauSimulationTemplate.cpp @@ -90,6 +90,7 @@ __DEFINE_REACTIONS_ Simulation simulation; Model* modelptr; modelptr = &model; + simulation.type = TAU; simulation.model = modelptr; simulation.end_time = end_time; simulation.random_seed = random_seed; From 42343f1d8df5a90e72a85fe792785f6d86b1d44b Mon Sep 17 00:00:00 2001 From: Matthew Dippel Date: Mon, 29 Mar 2021 14:00:42 -0400 Subject: [PATCH 11/88] finish simulationINIT --- gillespy2/solvers/cpp/c_base/model.h | 5 +++-- gillespy2/solvers/cpp/c_base/ode_cpp_solver/ODETemplate.cpp | 2 +- .../cpp/c_base/ode_cpp_solver/VariableODETemplate.cpp | 2 +- .../solvers/cpp/c_base/ssa_cpp_solver/SimulationTemplate.cpp | 2 +- .../cpp/c_base/ssa_cpp_solver/VariableSimulationTemplate.cpp | 2 +- .../cpp/c_base/tau_hybrid_cpp_solver/TauHybridTemplate.cpp | 2 +- .../tau_hybrid_cpp_solver/VariableTauHybridTemplate.cpp | 2 +- .../c_base/tau_leaping_cpp_solver/TauSimulationTemplate.cpp | 2 +- .../tau_leaping_cpp_solver/VariableTauSimulationTemplate.cpp | 2 +- 9 files changed, 11 insertions(+), 10 deletions(-) diff --git a/gillespy2/solvers/cpp/c_base/model.h b/gillespy2/solvers/cpp/c_base/model.h index 4defe5819..1d06b62d1 100644 --- a/gillespy2/solvers/cpp/c_base/model.h +++ b/gillespy2/solvers/cpp/c_base/model.h @@ -78,8 +78,9 @@ namespace Gillespy{ void output_results_buffer(std :: ostream& os); }; //Trajectory initializers for ODE and SSA solvers - void simulationODEINIT(Model* model, Simulation &simulation); - void simulationSSAINIT(Model* model, Simulation &simulation); + // void simulationODEINIT(Model* model, Simulation &simulation); + // void simulationSSAINIT(Model* model, Simulation &simulation); + void simulationINIT(Model* model, Simulation &simulation); } diff --git a/gillespy2/solvers/cpp/c_base/ode_cpp_solver/ODETemplate.cpp b/gillespy2/solvers/cpp/c_base/ode_cpp_solver/ODETemplate.cpp index e2507e39e..e343d5b91 100644 --- a/gillespy2/solvers/cpp/c_base/ode_cpp_solver/ODETemplate.cpp +++ b/gillespy2/solvers/cpp/c_base/ode_cpp_solver/ODETemplate.cpp @@ -90,7 +90,7 @@ __DEFINE_REACTIONS_ simulation.number_timesteps = number_timesteps; simulation.number_trajectories = number_trajectories; simulation.propensity_function = propFun; - simulationODEINIT(&model, simulation); + simulationINIT(&model, simulation); // Perform ODE // ODESolver(&simulation,increment); simulation.output_results_buffer(std :: cout); diff --git a/gillespy2/solvers/cpp/c_base/ode_cpp_solver/VariableODETemplate.cpp b/gillespy2/solvers/cpp/c_base/ode_cpp_solver/VariableODETemplate.cpp index 4307f7c4e..3a7df9dc0 100644 --- a/gillespy2/solvers/cpp/c_base/ode_cpp_solver/VariableODETemplate.cpp +++ b/gillespy2/solvers/cpp/c_base/ode_cpp_solver/VariableODETemplate.cpp @@ -102,7 +102,7 @@ __DEFINE_REACTIONS_ simulation.number_timesteps = number_timesteps; simulation.number_trajectories = number_trajectories; simulation.propensity_function = propFun; - simulationODEINIT(&model, simulation); + simulationINIT(&model, simulation); // Perform ODE // ODESolver(&simulation,increment); simulation.output_results_buffer(std :: cout); diff --git a/gillespy2/solvers/cpp/c_base/ssa_cpp_solver/SimulationTemplate.cpp b/gillespy2/solvers/cpp/c_base/ssa_cpp_solver/SimulationTemplate.cpp index 3ca0697cb..8145993ea 100644 --- a/gillespy2/solvers/cpp/c_base/ssa_cpp_solver/SimulationTemplate.cpp +++ b/gillespy2/solvers/cpp/c_base/ssa_cpp_solver/SimulationTemplate.cpp @@ -84,7 +84,7 @@ __DEFINE_REACTIONS_ simulation.number_timesteps = number_timesteps; simulation.number_trajectories = number_trajectories; simulation.propensity_function = propFun; - simulationSSAINIT(&model, simulation); + simulationINIT(&model, simulation); // Perform SSA // ssa_direct(&simulation); diff --git a/gillespy2/solvers/cpp/c_base/ssa_cpp_solver/VariableSimulationTemplate.cpp b/gillespy2/solvers/cpp/c_base/ssa_cpp_solver/VariableSimulationTemplate.cpp index 478dd429b..1bde6b826 100644 --- a/gillespy2/solvers/cpp/c_base/ssa_cpp_solver/VariableSimulationTemplate.cpp +++ b/gillespy2/solvers/cpp/c_base/ssa_cpp_solver/VariableSimulationTemplate.cpp @@ -92,7 +92,7 @@ __DEFINE_REACTIONS_ simulation.number_timesteps = number_timesteps; simulation.number_trajectories = number_trajectories; simulation.propensity_function = propFun; - simulationSSAINIT(&model, simulation); + simulationINIT(&model, simulation); // Perform SSA // ssa_direct(&simulation); diff --git a/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/TauHybridTemplate.cpp b/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/TauHybridTemplate.cpp index 6e2ddbda5..e8e65ea35 100644 --- a/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/TauHybridTemplate.cpp +++ b/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/TauHybridTemplate.cpp @@ -90,7 +90,7 @@ __DEFINE_REACTIONS_ simulation.number_timesteps = number_timesteps; simulation.number_trajectories = number_trajectories; simulation.propensity_function = propFun; - simulationODEINIT(&model, simulation); + simulationINIT(&model, simulation); // Perform ODE // // TauHybridCSolver(&simulation,); simulation.output_results_buffer(std :: cout); diff --git a/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/VariableTauHybridTemplate.cpp b/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/VariableTauHybridTemplate.cpp index 022787323..a6163de3a 100644 --- a/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/VariableTauHybridTemplate.cpp +++ b/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/VariableTauHybridTemplate.cpp @@ -102,7 +102,7 @@ __DEFINE_REACTIONS_ simulation.number_timesteps = number_timesteps; simulation.number_trajectories = number_trajectories; simulation.propensity_function = propFun; - simulationODEINIT(&model, simulation); + simulationINIT(&model, simulation); // Perform ODE // ODESolver(&simulation,increment); simulation.output_results_buffer(std :: cout); diff --git a/gillespy2/solvers/cpp/c_base/tau_leaping_cpp_solver/TauSimulationTemplate.cpp b/gillespy2/solvers/cpp/c_base/tau_leaping_cpp_solver/TauSimulationTemplate.cpp index 9aa5f1a7b..ccf652a49 100644 --- a/gillespy2/solvers/cpp/c_base/tau_leaping_cpp_solver/TauSimulationTemplate.cpp +++ b/gillespy2/solvers/cpp/c_base/tau_leaping_cpp_solver/TauSimulationTemplate.cpp @@ -91,7 +91,7 @@ __DEFINE_REACTIONS_ simulation.number_timesteps = number_timesteps; simulation.number_trajectories = number_trajectories; simulation.propensity_function = propFun; - simulationSSAINIT(&model, simulation); + simulationINIT(&model, simulation); // Perform Tau Leaping // tau_leaper(&simulation, tau_tol); simulation.output_results_buffer(std :: cout); diff --git a/gillespy2/solvers/cpp/c_base/tau_leaping_cpp_solver/VariableTauSimulationTemplate.cpp b/gillespy2/solvers/cpp/c_base/tau_leaping_cpp_solver/VariableTauSimulationTemplate.cpp index c77fd7559..97e924df7 100644 --- a/gillespy2/solvers/cpp/c_base/tau_leaping_cpp_solver/VariableTauSimulationTemplate.cpp +++ b/gillespy2/solvers/cpp/c_base/tau_leaping_cpp_solver/VariableTauSimulationTemplate.cpp @@ -97,7 +97,7 @@ __DEFINE_REACTIONS_ simulation.number_timesteps = number_timesteps; simulation.number_trajectories = number_trajectories; simulation.propensity_function = propFun; - simulationSSAINIT(&model, simulation); + simulationINIT(&model, simulation); // Perform Tau Leaping // tau_leaper(&simulation, tau_tol); simulation.output_results_buffer(std :: cout); From 3486915ece40bfe2edfe53f0704aeed3bc07f122 Mon Sep 17 00:00:00 2001 From: Matthew Dippel Date: Sun, 4 Apr 2021 22:08:25 -0400 Subject: [PATCH 12/88] add mode to species --- gillespy2/solvers/cpp/c_base/model.h | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/gillespy2/solvers/cpp/c_base/model.h b/gillespy2/solvers/cpp/c_base/model.h index 1d06b62d1..5b5a1f1a9 100644 --- a/gillespy2/solvers/cpp/c_base/model.h +++ b/gillespy2/solvers/cpp/c_base/model.h @@ -7,12 +7,15 @@ #include namespace Gillespy{ - + #define CONTINUOUS 0 + #define DISCRETE 1 //Represents info for a chemical reactant/product struct Species{ unsigned int id; //useful for index id in arrays std :: string name; unsigned int initial_population; + // CONTINUOUS or DISCRETE - used for hybrid solver + unsigned int mode; //Used for hashing into set, for TauLeapingCSolver bool operator < (const Species &other) const { return id < other.id; } }; From 618cb0fc7d214e5addaa4b168a1f786bf06d7818 Mon Sep 17 00:00:00 2001 From: Matthew Dippel Date: Mon, 5 Apr 2021 01:04:12 -0400 Subject: [PATCH 13/88] account for discrete/continuous modes in Species --- gillespy2/solvers/cpp/c_base/model.cpp | 27 +++++++++---- gillespy2/solvers/cpp/c_base/model.h | 19 +++++++-- .../TauHybridCSolver.cpp | 39 ++++++++++++++++--- 3 files changed, 69 insertions(+), 16 deletions(-) diff --git a/gillespy2/solvers/cpp/c_base/model.cpp b/gillespy2/solvers/cpp/c_base/model.cpp index 38e9a4b41..352886fbe 100644 --- a/gillespy2/solvers/cpp/c_base/model.cpp +++ b/gillespy2/solvers/cpp/c_base/model.cpp @@ -125,14 +125,27 @@ void simulationINIT(Model* model, Simulation &sim) { void Simulation :: output_results_buffer(std::ostream& os){ for (int i = 0 ; i < number_trajectories; i++){ - for (int j = 0; jnumber_species; k++){ - if (type==ODE){os<number_species; k++){ + if (type == ODE){ + os<model)->number_species; - uint32_t num_reactions = (simulation->model)->number_reactions; + int num_species = (simulation->model)->number_species; + int num_reactions = (simulation->model)->number_reactions; + int num_trajectories = simulation->number_trajectories; Model &model = *(simulation->model); + // std::unique_ptr species = model.species;?? TauArgs tau_args = initialize(*(simulation->model),tau_tol); double increment = simulation->timeline[1] - simulation->timeline[0]; @@ -85,11 +88,35 @@ namespace Gillespy { std::vector propensity_values(num_reactions); //copy initial state for each trajectory - for(uint32_t s = 0; s < num_species; s++){ + for(int s = 0; s < num_species; s++){ simulation->trajectories[0][0][s] = model.species[s].initial_population; } - //Simulate for each trajectory + //make new method here + for(int traj = 0; traj < num_trajectories; traj++){ + if (interrupted){ + break; + } + + for (int s = 0; s < num_species; s++) { + current_state[s] = model.species[s].initial_population; + } + simulation->current_time = 0; + //what is this? + int entry_count = 0; + //propensity sum is... + double propensity_sum; + //save time is... + double save_time = 0; + // steps rejected is... + int steps_rejected = 0; + //tau_step is... + double tau_step; + + std::vector prev_curr_state; + + while (entry_count < simulation->number_timesteps) + } } } } @@ -127,7 +154,7 @@ static int f(realtype t, N_Vector y, N_Vector y_dot, void *user_data) { } } } - - return(0); } + + From bb7faeb735d9d8e6222badc4c20fa6010af7a7da Mon Sep 17 00:00:00 2001 From: Matthew Dippel Date: Sun, 11 Apr 2021 21:02:43 -0400 Subject: [PATCH 14/88] naming simplification --- .../cpp/c_base/tau_hybrid_cpp_solver/TauHybridCSolver.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/TauHybridCSolver.cpp b/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/TauHybridCSolver.cpp index c5ab89065..8611322a7 100644 --- a/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/TauHybridCSolver.cpp +++ b/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/TauHybridCSolver.cpp @@ -78,7 +78,7 @@ namespace Gillespy { int num_reactions = (simulation->model)->number_reactions; int num_trajectories = simulation->number_trajectories; Model &model = *(simulation->model); - // std::unique_ptr species = model.species;?? + std::unique_ptr &species = model.species; TauArgs tau_args = initialize(*(simulation->model),tau_tol); double increment = simulation->timeline[1] - simulation->timeline[0]; @@ -89,7 +89,7 @@ namespace Gillespy { //copy initial state for each trajectory for(int s = 0; s < num_species; s++){ - simulation->trajectories[0][0][s] = model.species[s].initial_population; + simulation->trajectories[0][0][s] = species[s].initial_population; } //Simulate for each trajectory //make new method here @@ -99,7 +99,7 @@ namespace Gillespy { } for (int s = 0; s < num_species; s++) { - current_state[s] = model.species[s].initial_population; + current_state[s] = species[s].initial_population; } simulation->current_time = 0; //what is this? From d46320a2d542d12709d89d3588ec9764d6173d86 Mon Sep 17 00:00:00 2001 From: Matthew Dippel Date: Sun, 11 Apr 2021 22:39:15 -0400 Subject: [PATCH 15/88] partition species --- gillespy2/solvers/cpp/c_base/model.h | 3 ++- .../tau_hybrid_cpp_solver/TauHybridCSolver.cpp | 14 ++++++++------ gillespy2/solvers/numpy/tau_hybrid_solver.py | 5 +++++ 3 files changed, 15 insertions(+), 7 deletions(-) diff --git a/gillespy2/solvers/cpp/c_base/model.h b/gillespy2/solvers/cpp/c_base/model.h index a8d35bf07..0ea93c38e 100644 --- a/gillespy2/solvers/cpp/c_base/model.h +++ b/gillespy2/solvers/cpp/c_base/model.h @@ -9,6 +9,7 @@ namespace Gillespy{ #define CONTINUOUS 0 #define DISCRETE 1 + #define DYNAMIC 2 //Represents info for a chemical reactant/product struct Species{ unsigned int id; //useful for index id in arrays @@ -33,7 +34,7 @@ namespace Gillespy{ //Represents a model of reactions and species struct Model{ - void update_affected_reactions(); + void update_affected_reactions(); unsigned int number_species; std :: unique_ptr species; unsigned int number_reactions; diff --git a/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/TauHybridCSolver.cpp b/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/TauHybridCSolver.cpp index 8611322a7..d81d32228 100644 --- a/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/TauHybridCSolver.cpp +++ b/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/TauHybridCSolver.cpp @@ -39,12 +39,15 @@ namespace Gillespy { { interrupted = true; } - std::pair, double> get_reactions(const Gillespy::Model *model, const std::vector &propensity_values, double tau_step, double current_time, double save_time) + void partition_species(const Model &model, const std::vector &propensity_values ){ + + } + std::pair, double> get_reactions(const Gillespy::Model *model, const std::vector &propensity_values, double tau_step, double current_time, double save_time) { /* - * Helper Function to get reactions fired from t to t+tau. Effects two values: - *rxn_count - dict with key=Reaction channel value=number of times fired - *curr_time - float representing current time + * Helper Function to get reactions fired from t to t+tau. Affects two values: + * rxn_count - dict with key=Reaction channel value=number of times fired + * curr_time - float representing current time */ if (current_time + tau_step > save_time) @@ -115,7 +118,7 @@ namespace Gillespy { std::vector prev_curr_state; - while (entry_count < simulation->number_timesteps) + // while (entry_count < simulation->number_timesteps) } } } @@ -157,4 +160,3 @@ static int f(realtype t, N_Vector y, N_Vector y_dot, void *user_data) { return(0); } - diff --git a/gillespy2/solvers/numpy/tau_hybrid_solver.py b/gillespy2/solvers/numpy/tau_hybrid_solver.py index b9018e6e2..b9380257c 100644 --- a/gillespy2/solvers/numpy/tau_hybrid_solver.py +++ b/gillespy2/solvers/numpy/tau_hybrid_solver.py @@ -149,8 +149,13 @@ def __flag_det_reactions(self, model, det_spec, det_rxn, dependencies): # Determine if each rxn would be deterministic apart from other reactions prev_state = det_rxn.copy() for rxn in model.listOfReactions: + # assume it is deterministic det_rxn[rxn] = True + # iterate through the dependent species of this reaction for species in dependencies[rxn]: + # if any of the dependencies are discrete or (dynamic AND the + # species itself has not been flagged as deterministic) + # then allow it to be modelled discretely if model.listOfSpecies[species].mode == 'discrete': det_rxn[rxn] = False break From 2f2ec83964718e6016947ae5fbc2ed65fe333247 Mon Sep 17 00:00:00 2001 From: Matthew Dippel Date: Sun, 11 Apr 2021 23:34:17 -0400 Subject: [PATCH 16/88] init_species_mode() --- gillespy2/solvers/cpp/c_base/model.h | 16 +++++++++++----- .../tau_hybrid_cpp_solver/TauHybridCSolver.cpp | 18 ++++++++++++++++-- 2 files changed, 27 insertions(+), 7 deletions(-) diff --git a/gillespy2/solvers/cpp/c_base/model.h b/gillespy2/solvers/cpp/c_base/model.h index 0ea93c38e..d32bacb5a 100644 --- a/gillespy2/solvers/cpp/c_base/model.h +++ b/gillespy2/solvers/cpp/c_base/model.h @@ -15,12 +15,18 @@ namespace Gillespy{ unsigned int id; //useful for index id in arrays std :: string name; unsigned int initial_population; - // allows the user to specify if a species' population should definitely be modeled continuously or discretely - // CONTINUOUS or DISCRETE - used for hybrid solver + // Used for hybrid solver + // allows the user to specify if a species' population should definitely be modeled continuously or + // discretely + // CONTINUOUS or DISCRETE + // otherwise, mode will be determined by the program (DYNAMIC) + // if no choice is made, DYNAMIC will be assumed int user_mode; - // otherwise, mode will be determined by the program and this flag will switch back and forth between continuous and discrete through the runtime of the simulation - // CONTINUOUS or DISCRETE - used for hybrid solver - int dynamic_mode; + // during simulation execution, a species will fall into either of the two categories, CONTINUOUS or DISCRETE + // this is pre-determined only if the user_mode specifies CONTINUOUS or DISCRETE. + // otherwise, if DYNAMIC is specified, partition_mode will be continually calculated throughout the simulation + // according to standard deviation and coefficient of variance. + int partition_mode; //Used for hashing into set, for TauLeapingCSolver bool operator < (const Species &other) const { return id < other.id; } }; diff --git a/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/TauHybridCSolver.cpp b/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/TauHybridCSolver.cpp index d81d32228..2a83c671d 100644 --- a/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/TauHybridCSolver.cpp +++ b/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/TauHybridCSolver.cpp @@ -39,8 +39,22 @@ namespace Gillespy { { interrupted = true; } - void partition_species(const Model &model, const std::vector &propensity_values ){ - + void init_species_mode(const Model &model){ + int num_species = model.number_species; + for (int s = 0; s < num_species; s++){ + // if the user chooses discrete, initialise the partition flag to such. + if (model.species[s].user_mode == DISCRETE){ + model.species[s].partition_mode = DISCRETE; + } + // otherwise, either the user chose continuous or dynamic (or null). + // in any case, just initialise to continuous. + else { + model.species[s].partition_mode = CONTINUOUS; + } + } + } + void partition_species(const Model &model, const std::vector &propensity_values, double tau_step, double current_time) + { } std::pair, double> get_reactions(const Gillespy::Model *model, const std::vector &propensity_values, double tau_step, double current_time, double save_time) { From 054c41fb78149eb4bcbdae0bef915fdbefb0ec78 Mon Sep 17 00:00:00 2001 From: Matthew Dippel Date: Sun, 18 Apr 2021 20:27:49 -0400 Subject: [PATCH 17/88] create hybrid state vector --- .../TauHybridCSolver.cpp | 27 ++++++++++++++++--- 1 file changed, 23 insertions(+), 4 deletions(-) diff --git a/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/TauHybridCSolver.cpp b/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/TauHybridCSolver.cpp index 2a83c671d..3f00d47d7 100644 --- a/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/TauHybridCSolver.cpp +++ b/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/TauHybridCSolver.cpp @@ -39,8 +39,9 @@ namespace Gillespy { { interrupted = true; } - void init_species_mode(const Model &model){ + void init_species_mode(const Model &model, Simulation &simulation){ int num_species = model.number_species; + // int num_det_species = 0; for (int s = 0; s < num_species; s++){ // if the user chooses discrete, initialise the partition flag to such. if (model.species[s].user_mode == DISCRETE){ @@ -50,11 +51,25 @@ namespace Gillespy { // in any case, just initialise to continuous. else { model.species[s].partition_mode = CONTINUOUS; + // num_det_species++; } } + // simulation.number_det_species = num_det_species; } - void partition_species(const Model &model, const std::vector &propensity_values, double tau_step, double current_time) - { + void partition_species(const Model &model, const std::vector &propensity_values, std::vector curr_state, double tau_step, double current_time, std::map &det_species){ + // coefficient of variance- key:species id, value: cv + std::map cv; + // means + std::map means; + // standard deviation + std::map sd; + //init means + for (int i = 0; i < model.number_species; ++i){ + if (model.species[i].user_mode == DYNAMIC){ + cv.insert({i, curr_state[i]}); + } + } + } std::pair, double> get_reactions(const Gillespy::Model *model, const std::vector &propensity_values, double tau_step, double current_time, double save_time) { @@ -99,8 +114,12 @@ namespace Gillespy { TauArgs tau_args = initialize(*(simulation->model),tau_tol); double increment = simulation->timeline[1] - simulation->timeline[0]; + typedef union { + int discrete; + double continuous; + } hybrid_state; //initialize current_state vector to 0 for each species - std::vector current_state(num_species); + std::vector current_state(num_species); //initialize propensity_values to 0 for each species std::vector propensity_values(num_reactions); From 46a682fd5fe7e6a11208658762d727e9d9667b0e Mon Sep 17 00:00:00 2001 From: Matthew Dippel Date: Sun, 18 Apr 2021 20:59:59 -0400 Subject: [PATCH 18/88] correct state initialization --- .../cpp/c_base/tau_hybrid_cpp_solver/TauHybridCSolver.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/TauHybridCSolver.cpp b/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/TauHybridCSolver.cpp index 3f00d47d7..30ee00e5e 100644 --- a/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/TauHybridCSolver.cpp +++ b/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/TauHybridCSolver.cpp @@ -135,7 +135,11 @@ namespace Gillespy { } for (int s = 0; s < num_species; s++) { - current_state[s] = species[s].initial_population; + if (species[s].user_mode == DISCRETE){ + current_state[s].discrete = species[s].initial_population; + }else { + current_state[s].continuous = (double) species[s].initial_population; + } } simulation->current_time = 0; //what is this? From 0ff2ecb94aa129e9d1b1ca6f1f0c2efca686e6c6 Mon Sep 17 00:00:00 2001 From: Matthew Dippel Date: Sun, 18 Apr 2021 21:11:49 -0400 Subject: [PATCH 19/88] initalize means in partition --- .../tau_hybrid_cpp_solver/TauHybridCSolver.cpp | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/TauHybridCSolver.cpp b/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/TauHybridCSolver.cpp index 30ee00e5e..d501f1815 100644 --- a/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/TauHybridCSolver.cpp +++ b/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/TauHybridCSolver.cpp @@ -56,7 +56,7 @@ namespace Gillespy { } // simulation.number_det_species = num_det_species; } - void partition_species(const Model &model, const std::vector &propensity_values, std::vector curr_state, double tau_step, double current_time, std::map &det_species){ + void partition_species(const Model &model, const std::vector &propensity_values, std::vector curr_state, double tau_step, double current_time, std::map &det_species){ // coefficient of variance- key:species id, value: cv std::map cv; // means @@ -66,7 +66,11 @@ namespace Gillespy { //init means for (int i = 0; i < model.number_species; ++i){ if (model.species[i].user_mode == DYNAMIC){ - cv.insert({i, curr_state[i]}); + if (model.species[i].partition_mode == CONTINUOUS){ + cv.insert({i, curr_state[i].continuous}); + }else { + cv.insert({i, curr_state[i].discrete}); + } } } @@ -114,10 +118,7 @@ namespace Gillespy { TauArgs tau_args = initialize(*(simulation->model),tau_tol); double increment = simulation->timeline[1] - simulation->timeline[0]; - typedef union { - int discrete; - double continuous; - } hybrid_state; + //initialize current_state vector to 0 for each species std::vector current_state(num_species); //initialize propensity_values to 0 for each species From 4b3e43463433353a6ba62205d7dbdbafbfd23215 Mon Sep 17 00:00:00 2001 From: Matthew Dippel Date: Sun, 18 Apr 2021 21:14:26 -0400 Subject: [PATCH 20/88] initialize standard deviations --- .../cpp/c_base/tau_hybrid_cpp_solver/TauHybridCSolver.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/TauHybridCSolver.cpp b/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/TauHybridCSolver.cpp index d501f1815..ca39d6363 100644 --- a/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/TauHybridCSolver.cpp +++ b/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/TauHybridCSolver.cpp @@ -73,6 +73,13 @@ namespace Gillespy { } } } + //init sd's + for (int i = 0; i < model.number_species; ++i){ + if (model.species[i].user_mode == DYNAMIC){ + sd.insert({i, 0}); + } + } + } std::pair, double> get_reactions(const Gillespy::Model *model, const std::vector &propensity_values, double tau_step, double current_time, double save_time) From ab0c84958c0938167d69955af00f7c02dadb9ede Mon Sep 17 00:00:00 2001 From: Josh C Date: Sun, 18 Apr 2021 23:09:07 -0400 Subject: [PATCH 21/88] Init platform utils helper script - Creates platformutils.py, for putting platform-specific wrapper functions - Adds Popen wrapper (`open_simulation`) to "inject" platform-specific kwargs --- gillespy2/solvers/cpp/ssa_c_solver.py | 6 ++--- gillespy2/solvers/utilities/platformutils.py | 25 ++++++++++++++++++++ 2 files changed, 27 insertions(+), 4 deletions(-) create mode 100644 gillespy2/solvers/utilities/platformutils.py diff --git a/gillespy2/solvers/cpp/ssa_c_solver.py b/gillespy2/solvers/cpp/ssa_c_solver.py index 8320513df..3e04a83e1 100644 --- a/gillespy2/solvers/cpp/ssa_c_solver.py +++ b/gillespy2/solvers/cpp/ssa_c_solver.py @@ -1,6 +1,7 @@ import gillespy2 from gillespy2.core import gillespyError, GillesPySolver, log from gillespy2.solvers.utilities import solverutils as cutils +from gillespy2.solvers.utilities import platformutils import signal import time #for solver timeout implementation import os #for getting directories for C++ files @@ -209,15 +210,12 @@ def read_next(): # Windows event handling if os.name == "nt": sub_kill = lambda sim: sim.send_signal(signal.CTRL_BREAK_EVENT) - platform_args = { "creationflags": subprocess.CREATE_NEW_PROCESS_GROUP, - "start_new_session": True } # POSIX event handling else: sub_kill = lambda sim: os.killpg(sim.pid, signal.SIGINT) - platform_args = { "start_new_session": True } thread_events = { "timeout": False } - with subprocess.Popen(args, stdout=subprocess.PIPE, **platform_args) as simulation: + with platformutils.open_simulation(args, stdout=subprocess.PIPE) as simulation: # Put a timer on in the background, if a timeout was specified. def timeout_kill(): thread_events["timeout"] = True diff --git a/gillespy2/solvers/utilities/platformutils.py b/gillespy2/solvers/utilities/platformutils.py new file mode 100644 index 000000000..f4b26599e --- /dev/null +++ b/gillespy2/solvers/utilities/platformutils.py @@ -0,0 +1,25 @@ +""" +Collection of utility functions for performing platform-specific simulations. +Necessary to accomodate for things like signals and processes which vary between +Windows and POSIX platforms. + +Written By: Joshua Cooper +April 18, 2021 +""" + +import subprocess +import os + +def open_simulation(args, **kwargs): + """ + A thin wrapper around the Popen class which adds platform-specific args. + Pass arguments just like you would Popen. + """ + # Append universally-needed flags + kwargs["start_new_session"] = True + + # Windows-specific flags + if os.name == "nt": + kwargs["creationflags"] |= subprocess.CREATE_NEW_PROCESS_GROUP + + return subprocess.Popen(args, **kwargs) From 4215b6c00ab169095764c95bcc9f9376ff9eacf6 Mon Sep 17 00:00:00 2001 From: Josh C Date: Sun, 18 Apr 2021 23:19:33 -0400 Subject: [PATCH 22/88] Platform util function for `os.killpg` - Extracts the `sim_kill` lambda into a separate helper function --- gillespy2/solvers/cpp/ssa_c_solver.py | 11 ++--------- gillespy2/solvers/utilities/platformutils.py | 13 +++++++++++++ 2 files changed, 15 insertions(+), 9 deletions(-) diff --git a/gillespy2/solvers/cpp/ssa_c_solver.py b/gillespy2/solvers/cpp/ssa_c_solver.py index 3e04a83e1..616c61a85 100644 --- a/gillespy2/solvers/cpp/ssa_c_solver.py +++ b/gillespy2/solvers/cpp/ssa_c_solver.py @@ -206,20 +206,13 @@ def read_next(): # Buffer to store the output of the simulation (retrieved from sim_delegate thread). buffer = [] - # Each platform is given their own platform-specific sub_kill() function. - # Windows event handling - if os.name == "nt": - sub_kill = lambda sim: sim.send_signal(signal.CTRL_BREAK_EVENT) - # POSIX event handling - else: - sub_kill = lambda sim: os.killpg(sim.pid, signal.SIGINT) thread_events = { "timeout": False } with platformutils.open_simulation(args, stdout=subprocess.PIPE) as simulation: # Put a timer on in the background, if a timeout was specified. def timeout_kill(): thread_events["timeout"] = True - sub_kill(simulation) + platformutils.sub_kill(simulation) timeout_thread = threading.Timer(timeout, timeout_kill) try: return_code = 0 @@ -234,7 +227,7 @@ def timeout_kill(): while simulation.poll() is None: time.sleep(0.1) except KeyboardInterrupt: - sub_kill(simulation) + platformutils.sub_kill(simulation) pause = True return_code = 33 finally: diff --git a/gillespy2/solvers/utilities/platformutils.py b/gillespy2/solvers/utilities/platformutils.py index f4b26599e..d7911627a 100644 --- a/gillespy2/solvers/utilities/platformutils.py +++ b/gillespy2/solvers/utilities/platformutils.py @@ -8,6 +8,7 @@ """ import subprocess +import signal import os def open_simulation(args, **kwargs): @@ -23,3 +24,15 @@ def open_simulation(args, **kwargs): kwargs["creationflags"] |= subprocess.CREATE_NEW_PROCESS_GROUP return subprocess.Popen(args, **kwargs) + +def sub_kill(simulation: subprocess.Popen): + """ + Platform-independent wrapper around `os.kill` and similar functions. + Sends an interrupt signal/event to the given simulation. + """ + # Windows-specific interrupt + if os.name == "nt": + simulation.send_signal(signal.CTRL_BREAK_EVENT) + # POSIX interrupt + else: + os.killpg(simulation.pid, signal.SIGINT) From 1661e940fdf398673b499ec93f2c8c46d24d395f Mon Sep 17 00:00:00 2001 From: Josh C Date: Mon, 19 Apr 2021 10:31:40 -0400 Subject: [PATCH 23/88] Abstracts sim reader into custom class - Adds new class in platformutils: `SimulationReader` - Handles reading the output of simulation in background thread - Optionally handles timeout --- gillespy2/solvers/cpp/ssa_c_solver.py | 52 +++------------ gillespy2/solvers/utilities/platformutils.py | 69 ++++++++++++++++++++ 2 files changed, 77 insertions(+), 44 deletions(-) diff --git a/gillespy2/solvers/cpp/ssa_c_solver.py b/gillespy2/solvers/cpp/ssa_c_solver.py index 616c61a85..779bef74c 100644 --- a/gillespy2/solvers/cpp/ssa_c_solver.py +++ b/gillespy2/solvers/cpp/ssa_c_solver.py @@ -189,62 +189,26 @@ def run(self=None, model=None, t=20, number_of_trajectories=1, timeout=0, else: raise gillespyError.ModelError("seed must be a positive integer") - # Handler for reading data from subprocess, in background thread. - def sim_delegate(sim, sim_buffer): - def read_next(): - # Reads the next block from the simulation output. - # Returns the length of the string read. - line = sim.stdout.read().decode("utf-8") - ln = len(line) - if ln > 0: - sim_buffer.append(line) - return ln - # Read output 1 block at a time, until the program is finished. - page_size = read_next() - while page_size > 0 and sim.poll() is None: - page_size = read_next() - - # Buffer to store the output of the simulation (retrieved from sim_delegate thread). - buffer = [] - - thread_events = { "timeout": False } with platformutils.open_simulation(args, stdout=subprocess.PIPE) as simulation: - # Put a timer on in the background, if a timeout was specified. - def timeout_kill(): - thread_events["timeout"] = True - platformutils.sub_kill(simulation) - timeout_thread = threading.Timer(timeout, timeout_kill) + return_code = 0 + reader = platformutils.SimulationReader(simulation, timeout) try: - return_code = 0 - output_process = threading.Thread(name="SimulationHandlerThread", - target=sim_delegate, - args=(simulation, buffer)) - output_process.start() - if timeout > 0: - timeout_thread.start() - - # Poll for the program's status; keyboard interrupt is ignored if we use .wait() - while simulation.poll() is None: - time.sleep(0.1) + reader.start() + stdout, return_code = reader.read() except KeyboardInterrupt: platformutils.sub_kill(simulation) + stdout, return_code = reader.read() pause = True return_code = 33 finally: - # Finish off the output reader thread and the timer thread. - return_code = simulation.wait() - output_process.join() - if timeout_thread.is_alive(): - timeout_thread.cancel() - - # Decode from byte, split by comma into array - stdout = "".join(buffer).split(",") # Check if the simulation had been paused # (Necessary because we can't set pause to True from thread handler) - if thread_events["timeout"]: + if reader.timed_out: pause = True return_code = 33 + # Decode from byte, split by comma into array + stdout = stdout.split(",") if return_code in [0, 33]: trajectory_base, timeStopped = cutils.parse_binary_output(number_of_trajectories, number_timesteps, len(model.listOfSpecies), stdout, pause=pause) diff --git a/gillespy2/solvers/utilities/platformutils.py b/gillespy2/solvers/utilities/platformutils.py index d7911627a..633c1e96f 100644 --- a/gillespy2/solvers/utilities/platformutils.py +++ b/gillespy2/solvers/utilities/platformutils.py @@ -8,9 +8,78 @@ """ import subprocess +import threading +import time import signal import os +class SimulationReader(): + """ + """ + def __init__(self, simulation: subprocess.Popen, timeout=0): + self.simulation = simulation + self.buffer = [] + self.timed_out = False + + if timeout > 0: + self.timeout_thread = threading.Timer(timeout, self.__timeout_kill) + else: + self.timeout_thread = None + self.reader_thread = threading.Thread(name="SimulationReaderThread", + target=self.__reader_thread) + + def __timeout_kill(self): + """ + Handler to kill the simulation on timeout. + """ + self.timed_out = True + sub_kill(self.simulation) + + def __reader_thread(self): + """ + Handler for reading data from subprocess in background thread. + """ + def read_next(): + # Reads the next block from the simulation output. + # Returns the length of the string read. + line = self.simulation.stdout.read().decode("utf-8") + ln = len(line) + if ln > 0: + self.buffer.append(line) + return ln + # Read output 1 block at a time, until the program is finished. + page_size = read_next() + while page_size > 0 and self.simulation.poll() is None: + page_size = read_next() + + def start(self): + """ + Activates the reader by starting the processing/timeout threads. + Must be called before read(). + """ + if self.timeout_thread is not None: + self.timeout_thread.start() + self.reader_thread.start() + + def read(self) -> (str, int): + """ + Blocks and waits for the output of the simulation to finish. + Returns the output as a string, and the return code of the simulation. + """ + return_code = self.simulation.poll() + + while return_code is None: + return_code = self.simulation.poll() + time.sleep(0.1) + + self.reader_thread.join() + if self.timeout_thread is not None: + self.timeout_thread.cancel() + self.timeout_thread.join() + + return "".join(self.buffer), return_code + + def open_simulation(args, **kwargs): """ A thin wrapper around the Popen class which adds platform-specific args. From c6b8c6bce7b343dc6d0419391f5ca7b87caa92c2 Mon Sep 17 00:00:00 2001 From: Josh C Date: Mon, 19 Apr 2021 11:32:26 -0400 Subject: [PATCH 24/88] Platform-independent signal handlers in C solvers - Define signal handler info as macros - Use `INTERRUPT_*` macros as type definitions - Use `SET_INTERRUPT_HANDLER` as a wrapper around signal installer --- gillespy2/solvers/cpp/c_base/platform.h | 35 +++++++++++++++++++ .../solvers/cpp/c_base/ssa_cpp_solver/ssa.cpp | 30 +++++----------- 2 files changed, 44 insertions(+), 21 deletions(-) create mode 100644 gillespy2/solvers/cpp/c_base/platform.h diff --git a/gillespy2/solvers/cpp/c_base/platform.h b/gillespy2/solvers/cpp/c_base/platform.h new file mode 100644 index 000000000..00a4d9465 --- /dev/null +++ b/gillespy2/solvers/cpp/c_base/platform.h @@ -0,0 +1,35 @@ + +/* Platform-specific macros and functions between POSIX and Windows. + * Necessary to account for things like interrupt handlers, etc. + * + * Written by: Joshua Cooper + * April 19, 2021 + */ + +#ifndef __PLATFORM_H +#define __PLATFORM_H + +#ifdef _WIN32 +#include + +// Interrupt return type, signal number, and return value all differ between Windows and POSIX. +#define INTERRUPT_HANDLER BOOL WINAPI +#define INTERRUPT_SIGNUM DWORD +// Return TRUE on Windows means "this is the last event handler; do not continue." +// This is necessary to ensure that the simulation doesn't stop, so we can cleanly finish simulation. +#define INTERRUPT_RETURN return TRUE + +#define SET_INTERRUPT_HANDLER(fn) SetConsoleCtrlHandler(fn, TRUE) + +#else +#include + +#define INTERRUPT_HANDLER void +#define INTERRUPT_SIGNUM int +#define INTERRUPT_RETURN return + +#define SET_INTERRUPT_HANDLER(fn) signal(SIGINT, fn) + +#endif + +#endif diff --git a/gillespy2/solvers/cpp/c_base/ssa_cpp_solver/ssa.cpp b/gillespy2/solvers/cpp/c_base/ssa_cpp_solver/ssa.cpp index a995fe1e1..4a2738f64 100644 --- a/gillespy2/solvers/cpp/c_base/ssa_cpp_solver/ssa.cpp +++ b/gillespy2/solvers/cpp/c_base/ssa_cpp_solver/ssa.cpp @@ -1,34 +1,22 @@ #include "ssa.h" +#include "platform.h" #include //Included for mt19937 random number generator #include //Included for natural logarithm #include //Included for memcpy only #include //Included for timeout signal handling -#ifdef _WIN32 -#include -#endif +static volatile bool interrupted = false; -namespace Gillespy{ - - volatile bool interrupted = false ; +static INTERRUPT_HANDLER signalHandler(INTERRUPT_SIGNUM signum) +{ + interrupted = true; + INTERRUPT_RETURN; +} - #ifdef _WIN32 - BOOL WINAPI eventHandler(DWORD CtrlType) { - interrupted = true; - return TRUE; - } - #endif - - void signalHandler( int signum){ - interrupted = true ; - } +namespace Gillespy{ void ssa_direct(Simulation* simulation){ - #ifdef _WIN32 - SetConsoleCtrlHandler(eventHandler, TRUE); - #else - signal(SIGINT, signalHandler); - #endif + SET_INTERRUPT_HANDLER(signalHandler); if(simulation){ std :: mt19937_64 rng(simulation -> random_seed); From d35e32f26c28e70a5eff2e034e03ac71b13fba93 Mon Sep 17 00:00:00 2001 From: Matthew Dippel Date: Mon, 26 Apr 2021 11:48:17 -0400 Subject: [PATCH 25/88] calculate means and sd for dynamic species in rxn --- .../TauHybridCSolver.cpp | 23 ++++++++++++++++--- 1 file changed, 20 insertions(+), 3 deletions(-) diff --git a/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/TauHybridCSolver.cpp b/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/TauHybridCSolver.cpp index ca39d6363..f38c64ff0 100644 --- a/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/TauHybridCSolver.cpp +++ b/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/TauHybridCSolver.cpp @@ -67,9 +67,9 @@ namespace Gillespy { for (int i = 0; i < model.number_species; ++i){ if (model.species[i].user_mode == DYNAMIC){ if (model.species[i].partition_mode == CONTINUOUS){ - cv.insert({i, curr_state[i].continuous}); + means.insert({i, curr_state[i].continuous}); }else { - cv.insert({i, curr_state[i].discrete}); + means.insert({i, curr_state[i].discrete}); } } } @@ -80,7 +80,24 @@ namespace Gillespy { } } - + for (int r = 0; r < model.number_reactions; ++r){ + for (int s = 0; s < model.number_species; ++s){ + // access list of species by accessing the correct element of the state-change vector (of the reaction) + if (model.species[model.reactions[r].species_change[s]].user_mode == DYNAMIC ){ + // if less than 0, that means this is a reactant + if (model.reactions[r].species_change[s] < 0){ + means[s] -= (tau_step * propensity_values[r] * model.reactions[r].species_change[s]); + sd[s] += std::pow((tau_step * propensity_values[r] * model.reactions[r].species_change[s]),2); + } + // if greater than 0, that means this is a product + if (model.reactions[r].species_change[s] > 0){ + means[s] += (tau_step * propensity_values[r] * model.reactions[r].species_change[s]); + sd[s] += std::pow((tau_step * propensity_values[r] * model.reactions[r].species_change[s]),2); + } + } + } + } + } std::pair, double> get_reactions(const Gillespy::Model *model, const std::vector &propensity_values, double tau_step, double current_time, double save_time) { From da4fbce81775b893b19aa43889e61c02ee462729 Mon Sep 17 00:00:00 2001 From: Matthew Dippel Date: Mon, 26 Apr 2021 12:23:01 -0400 Subject: [PATCH 26/88] hybrid_state union typedef --- gillespy2/solvers/cpp/c_base/model.h | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/gillespy2/solvers/cpp/c_base/model.h b/gillespy2/solvers/cpp/c_base/model.h index d32bacb5a..d84bddecc 100644 --- a/gillespy2/solvers/cpp/c_base/model.h +++ b/gillespy2/solvers/cpp/c_base/model.h @@ -30,6 +30,11 @@ namespace Gillespy{ //Used for hashing into set, for TauLeapingCSolver bool operator < (const Species &other) const { return id < other.id; } }; + typedef union + { + int discrete; + double continuous; + } hybrid_state; struct Reaction{ unsigned int id; //useful for propensity function id associated From 90cd6a55cc6233b618cff1379e6f999a5acaf6bd Mon Sep 17 00:00:00 2001 From: Matthew Dippel Date: Mon, 26 Apr 2021 12:23:33 -0400 Subject: [PATCH 27/88] start coefficient of variation calculation --- .../c_base/tau_hybrid_cpp_solver/TauHybridCSolver.cpp | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/TauHybridCSolver.cpp b/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/TauHybridCSolver.cpp index f38c64ff0..97d23b1bd 100644 --- a/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/TauHybridCSolver.cpp +++ b/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/TauHybridCSolver.cpp @@ -79,7 +79,7 @@ namespace Gillespy { sd.insert({i, 0}); } } - + // calculate means and standard deviations for dynamic-mode species involved in reactions for (int r = 0; r < model.number_reactions; ++r){ for (int s = 0; s < model.number_species; ++s){ // access list of species by accessing the correct element of the state-change vector (of the reaction) @@ -97,7 +97,16 @@ namespace Gillespy { } } } + // calculate coefficient of variation using means and sd + for (int s = 0; s < model.number_species; ++s){ + if (means.count(s) > 0){ + Species sref = model.species[s]; + // if (sref.) + //need to implement switch_tol and switch_min in species + } + } + } std::pair, double> get_reactions(const Gillespy::Model *model, const std::vector &propensity_values, double tau_step, double current_time, double save_time) { From a34680d71171bc059dd831476f16a679618a2c41 Mon Sep 17 00:00:00 2001 From: Matthew Dippel Date: Mon, 26 Apr 2021 12:28:20 -0400 Subject: [PATCH 28/88] indentation --- gillespy2/solvers/cpp/c_base/model.cpp | 69 ++++++++++++-------------- 1 file changed, 32 insertions(+), 37 deletions(-) diff --git a/gillespy2/solvers/cpp/c_base/model.cpp b/gillespy2/solvers/cpp/c_base/model.cpp index 352886fbe..d5a37724f 100644 --- a/gillespy2/solvers/cpp/c_base/model.cpp +++ b/gillespy2/solvers/cpp/c_base/model.cpp @@ -18,7 +18,7 @@ namespace Gillespy{ reactions[i].name = reaction_names[i]; reactions[i].species_change = std :: make_unique(number_species); for(unsigned int j = 0; j < number_species; j++){ - reactions[i].species_change[j] = 0; + reactions[i].species_change[j] = 0; } reactions[i].affected_reactions = std :: vector(); } @@ -47,7 +47,7 @@ namespace Gillespy{ for(unsigned int i = 0; i < simulation.number_timesteps; i++){ simulation.timeline[i] = timestep_size * i; } - unsigned int trajectory_size = simulation.number_timesteps * (model -> number_species); + unsigned int trajectory_size = simulation.number_timesteps * (model -> number_species); simulation.trajectories_1D = new unsigned int[simulation.number_trajectories * trajectory_size]; simulation.trajectories = new unsigned int**[simulation.number_trajectories]; for(unsigned int i = 0; i < simulation.number_trajectories; i++){ @@ -59,7 +59,7 @@ namespace Gillespy{ } // initialize timeline, -void simulationODEINIT(Model* model, Simulation &simulation){ + void simulationODEINIT(Model* model, Simulation &simulation){ simulation.timeline = new double[simulation.number_timesteps]; double timestep_size = simulation.end_time/(simulation.number_timesteps-1); for(unsigned int i = 0; i < simulation.number_timesteps; i++){ @@ -71,22 +71,20 @@ void simulationODEINIT(Model* model, Simulation &simulation){ for(unsigned int i = 0; i < simulation.number_trajectories; i++){ simulation.trajectoriesODE[i] = new double*[simulation.number_timesteps]; for(unsigned int j = 0; j < simulation.number_timesteps; j++){ - simulation.trajectoriesODE[i][j] = &(simulation.trajectories_1DODE[i * trajectory_size + j * (model -> number_species)]); + simulation.trajectoriesODE[i][j] = &(simulation.trajectories_1DODE[i * trajectory_size + j * (model -> number_species)]); } } } -void simulationINIT(Model* model, Simulation &sim) { - int type = sim.type; - if (type == ODE || type == HYBRID) { - simulationODEINIT(model, sim); + void simulationINIT(Model* model, Simulation &sim) { + int type = sim.type; + if (type == ODE || type == HYBRID) { + simulationODEINIT(model, sim); + } + else if (type == SSA || type == TAU || type == HYBRID){ + simulationSSAINIT(model, sim); } - else if (type == SSA || type == TAU || type == HYBRID) - { - simulationSSAINIT(model, sim); } - -} Simulation :: ~Simulation(){ @@ -113,41 +111,38 @@ void simulationINIT(Model* model, Simulation &sim) { for(unsigned int i = 0; i < simulation.number_timesteps; i++){ os << simulation.timeline[i] << " "; for(unsigned int trajectory = 0; trajectory < simulation.number_trajectories; trajectory++){ - for(unsigned int j = 0; j < simulation.model -> number_species; j++){ - if (simulation.type==ODE){os << simulation.trajectoriesODE[trajectory][i][j] << " ";} - else{os << simulation.trajectories[trajectory][i][j] << " ";} - } + for(unsigned int j = 0; j < simulation.model -> number_species; j++){ + if (simulation.type==ODE){os << simulation.trajectoriesODE[trajectory][i][j] << " ";} + else{os << simulation.trajectories[trajectory][i][j] << " ";} + } } os << "\n"; } return os; } -void Simulation :: output_results_buffer(std::ostream& os){ + void Simulation :: output_results_buffer(std::ostream& os){ for (int i = 0 ; i < number_trajectories; i++){ for (int j = 0; j < number_timesteps; j++){ - os<number_species; k++){ - if (type == ODE){ - os<number_species; k++){ + if (type == ODE){ + os< Date: Mon, 26 Apr 2021 13:37:18 -0400 Subject: [PATCH 29/88] add switch_min, switch_tol to species --- gillespy2/solvers/cpp/c_base/model.h | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/gillespy2/solvers/cpp/c_base/model.h b/gillespy2/solvers/cpp/c_base/model.h index d84bddecc..3576d539c 100644 --- a/gillespy2/solvers/cpp/c_base/model.h +++ b/gillespy2/solvers/cpp/c_base/model.h @@ -15,7 +15,11 @@ namespace Gillespy{ unsigned int id; //useful for index id in arrays std :: string name; unsigned int initial_population; - // Used for hybrid solver + + //Used for hashing into set, for TauLeapingCSolver + bool operator < (const Species &other) const { return id < other.id; } + + // Everything below here used for hybrid solver // allows the user to specify if a species' population should definitely be modeled continuously or // discretely // CONTINUOUS or DISCRETE @@ -27,8 +31,13 @@ namespace Gillespy{ // otherwise, if DYNAMIC is specified, partition_mode will be continually calculated throughout the simulation // according to standard deviation and coefficient of variance. int partition_mode; - //Used for hashing into set, for TauLeapingCSolver - bool operator < (const Species &other) const { return id < other.id; } + // Tolerance level for considering a dynamic species deterministically, value is compared + // to an estimated sd/mean population of a species after a given time step. + // This value will be used if a switch_min is not provided. The default value is 0.03 + double switch_tol; + //Minimum population value at which species will be represented as continuous. + // If a value is given, switch_min will be used instead of switch_tol. + unsigned int switch_min; }; typedef union { From 2e5bde018134583d11ce8f06e2c2e0619c99c210 Mon Sep 17 00:00:00 2001 From: Matthew Dippel Date: Mon, 26 Apr 2021 13:41:37 -0400 Subject: [PATCH 30/88] default values --- gillespy2/solvers/cpp/c_base/model.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/gillespy2/solvers/cpp/c_base/model.h b/gillespy2/solvers/cpp/c_base/model.h index 3576d539c..273d8dbc9 100644 --- a/gillespy2/solvers/cpp/c_base/model.h +++ b/gillespy2/solvers/cpp/c_base/model.h @@ -34,10 +34,10 @@ namespace Gillespy{ // Tolerance level for considering a dynamic species deterministically, value is compared // to an estimated sd/mean population of a species after a given time step. // This value will be used if a switch_min is not provided. The default value is 0.03 - double switch_tol; + double switch_tol = 0.03; //Minimum population value at which species will be represented as continuous. // If a value is given, switch_min will be used instead of switch_tol. - unsigned int switch_min; + unsigned int switch_min = 0; }; typedef union { From d412f92c20ffa24014c6abd9d49b1fdbaad62c65 Mon Sep 17 00:00:00 2001 From: Matthew Dippel Date: Mon, 26 Apr 2021 13:52:54 -0400 Subject: [PATCH 31/88] flag species according to computed CV --- .../TauHybridCSolver.cpp | 22 ++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/TauHybridCSolver.cpp b/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/TauHybridCSolver.cpp index 97d23b1bd..67380e88b 100644 --- a/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/TauHybridCSolver.cpp +++ b/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/TauHybridCSolver.cpp @@ -102,11 +102,27 @@ namespace Gillespy { for (int s = 0; s < model.number_species; ++s){ if (means.count(s) > 0){ Species sref = model.species[s]; - // if (sref.) - //need to implement switch_tol and switch_min in species + if (sref.switch_min == 0) { // (default value means switch min not set, use switch tol) + if (means[s] > 0){ + cv[s] = sd[s] / means[s]; + }else{ + cv[s] = 1; + } + if (cv[s] < sref.switch_tol){ + sref.partition_mode == CONTINUOUS; + }else{ + sref.partition_mode == DISCRETE; + } + }else{ + if (means[s] > sref.switch_min){ + sref.partition_mode == CONTINUOUS; + }else{ + sref.partition_mode == DISCRETE; + } + } } } - + return //TODO; } std::pair, double> get_reactions(const Gillespy::Model *model, const std::vector &propensity_values, double tau_step, double current_time, double save_time) { From 7a1369a50ccb80d7483d1673b7a1b77130c98da8 Mon Sep 17 00:00:00 2001 From: Matthew Dippel Date: Mon, 10 May 2021 12:57:24 -0400 Subject: [PATCH 32/88] fix travis complaint --- gillespy2/solvers/cpp/c_base/model.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gillespy2/solvers/cpp/c_base/model.cpp b/gillespy2/solvers/cpp/c_base/model.cpp index d5a37724f..6ffa954f7 100644 --- a/gillespy2/solvers/cpp/c_base/model.cpp +++ b/gillespy2/solvers/cpp/c_base/model.cpp @@ -129,7 +129,7 @@ namespace Gillespy{ if (type == ODE){ os< Date: Mon, 10 May 2021 16:06:10 -0400 Subject: [PATCH 33/88] Revert platform utils - Too many changes; will be placing into a separate branch --- gillespy2/solvers/cpp/c_base/platform.h | 35 ------ .../solvers/cpp/c_base/ssa_cpp_solver/ssa.cpp | 30 +++-- gillespy2/solvers/utilities/platformutils.py | 107 ------------------ 3 files changed, 21 insertions(+), 151 deletions(-) delete mode 100644 gillespy2/solvers/cpp/c_base/platform.h delete mode 100644 gillespy2/solvers/utilities/platformutils.py diff --git a/gillespy2/solvers/cpp/c_base/platform.h b/gillespy2/solvers/cpp/c_base/platform.h deleted file mode 100644 index 00a4d9465..000000000 --- a/gillespy2/solvers/cpp/c_base/platform.h +++ /dev/null @@ -1,35 +0,0 @@ - -/* Platform-specific macros and functions between POSIX and Windows. - * Necessary to account for things like interrupt handlers, etc. - * - * Written by: Joshua Cooper - * April 19, 2021 - */ - -#ifndef __PLATFORM_H -#define __PLATFORM_H - -#ifdef _WIN32 -#include - -// Interrupt return type, signal number, and return value all differ between Windows and POSIX. -#define INTERRUPT_HANDLER BOOL WINAPI -#define INTERRUPT_SIGNUM DWORD -// Return TRUE on Windows means "this is the last event handler; do not continue." -// This is necessary to ensure that the simulation doesn't stop, so we can cleanly finish simulation. -#define INTERRUPT_RETURN return TRUE - -#define SET_INTERRUPT_HANDLER(fn) SetConsoleCtrlHandler(fn, TRUE) - -#else -#include - -#define INTERRUPT_HANDLER void -#define INTERRUPT_SIGNUM int -#define INTERRUPT_RETURN return - -#define SET_INTERRUPT_HANDLER(fn) signal(SIGINT, fn) - -#endif - -#endif diff --git a/gillespy2/solvers/cpp/c_base/ssa_cpp_solver/ssa.cpp b/gillespy2/solvers/cpp/c_base/ssa_cpp_solver/ssa.cpp index 4a2738f64..a995fe1e1 100644 --- a/gillespy2/solvers/cpp/c_base/ssa_cpp_solver/ssa.cpp +++ b/gillespy2/solvers/cpp/c_base/ssa_cpp_solver/ssa.cpp @@ -1,22 +1,34 @@ #include "ssa.h" -#include "platform.h" #include //Included for mt19937 random number generator #include //Included for natural logarithm #include //Included for memcpy only #include //Included for timeout signal handling -static volatile bool interrupted = false; - -static INTERRUPT_HANDLER signalHandler(INTERRUPT_SIGNUM signum) -{ - interrupted = true; - INTERRUPT_RETURN; -} +#ifdef _WIN32 +#include +#endif namespace Gillespy{ + volatile bool interrupted = false ; + + #ifdef _WIN32 + BOOL WINAPI eventHandler(DWORD CtrlType) { + interrupted = true; + return TRUE; + } + #endif + + void signalHandler( int signum){ + interrupted = true ; + } + void ssa_direct(Simulation* simulation){ - SET_INTERRUPT_HANDLER(signalHandler); + #ifdef _WIN32 + SetConsoleCtrlHandler(eventHandler, TRUE); + #else + signal(SIGINT, signalHandler); + #endif if(simulation){ std :: mt19937_64 rng(simulation -> random_seed); diff --git a/gillespy2/solvers/utilities/platformutils.py b/gillespy2/solvers/utilities/platformutils.py deleted file mode 100644 index 633c1e96f..000000000 --- a/gillespy2/solvers/utilities/platformutils.py +++ /dev/null @@ -1,107 +0,0 @@ -""" -Collection of utility functions for performing platform-specific simulations. -Necessary to accomodate for things like signals and processes which vary between -Windows and POSIX platforms. - -Written By: Joshua Cooper -April 18, 2021 -""" - -import subprocess -import threading -import time -import signal -import os - -class SimulationReader(): - """ - """ - def __init__(self, simulation: subprocess.Popen, timeout=0): - self.simulation = simulation - self.buffer = [] - self.timed_out = False - - if timeout > 0: - self.timeout_thread = threading.Timer(timeout, self.__timeout_kill) - else: - self.timeout_thread = None - self.reader_thread = threading.Thread(name="SimulationReaderThread", - target=self.__reader_thread) - - def __timeout_kill(self): - """ - Handler to kill the simulation on timeout. - """ - self.timed_out = True - sub_kill(self.simulation) - - def __reader_thread(self): - """ - Handler for reading data from subprocess in background thread. - """ - def read_next(): - # Reads the next block from the simulation output. - # Returns the length of the string read. - line = self.simulation.stdout.read().decode("utf-8") - ln = len(line) - if ln > 0: - self.buffer.append(line) - return ln - # Read output 1 block at a time, until the program is finished. - page_size = read_next() - while page_size > 0 and self.simulation.poll() is None: - page_size = read_next() - - def start(self): - """ - Activates the reader by starting the processing/timeout threads. - Must be called before read(). - """ - if self.timeout_thread is not None: - self.timeout_thread.start() - self.reader_thread.start() - - def read(self) -> (str, int): - """ - Blocks and waits for the output of the simulation to finish. - Returns the output as a string, and the return code of the simulation. - """ - return_code = self.simulation.poll() - - while return_code is None: - return_code = self.simulation.poll() - time.sleep(0.1) - - self.reader_thread.join() - if self.timeout_thread is not None: - self.timeout_thread.cancel() - self.timeout_thread.join() - - return "".join(self.buffer), return_code - - -def open_simulation(args, **kwargs): - """ - A thin wrapper around the Popen class which adds platform-specific args. - Pass arguments just like you would Popen. - """ - # Append universally-needed flags - kwargs["start_new_session"] = True - - # Windows-specific flags - if os.name == "nt": - kwargs["creationflags"] |= subprocess.CREATE_NEW_PROCESS_GROUP - - return subprocess.Popen(args, **kwargs) - -def sub_kill(simulation: subprocess.Popen): - """ - Platform-independent wrapper around `os.kill` and similar functions. - Sends an interrupt signal/event to the given simulation. - """ - # Windows-specific interrupt - if os.name == "nt": - simulation.send_signal(signal.CTRL_BREAK_EVENT) - # POSIX interrupt - else: - os.killpg(simulation.pid, signal.SIGINT) From 6532a484643a3f0bb4cd06b0c95b3c35b1983170 Mon Sep 17 00:00:00 2001 From: Matthew Dippel Date: Mon, 10 May 2021 18:44:30 -0400 Subject: [PATCH 34/88] add tau_args to simulation --- gillespy2/solvers/cpp/c_base/model.h | 6 +- .../tau_leaping_cpp_solver/tau_leaper.cpp | 185 +----------------- 2 files changed, 13 insertions(+), 178 deletions(-) diff --git a/gillespy2/solvers/cpp/c_base/model.h b/gillespy2/solvers/cpp/c_base/model.h index 273d8dbc9..f03e02f8e 100644 --- a/gillespy2/solvers/cpp/c_base/model.h +++ b/gillespy2/solvers/cpp/c_base/model.h @@ -5,6 +5,7 @@ #include #include #include +#include "./Tau/tau.h" namespace Gillespy{ #define CONTINUOUS 0 @@ -84,11 +85,12 @@ namespace Gillespy{ int type; // array representing discrete time steps for the simulation double* timeline; - // + double end_time; double current_time; int random_seed; - + // for Tau-Leaping and Hybrid simulations + TauArgs tau_args; unsigned int number_timesteps; unsigned int number_trajectories; diff --git a/gillespy2/solvers/cpp/c_base/tau_leaping_cpp_solver/tau_leaper.cpp b/gillespy2/solvers/cpp/c_base/tau_leaping_cpp_solver/tau_leaper.cpp index 5220caf29..7c00643c8 100644 --- a/gillespy2/solvers/cpp/c_base/tau_leaping_cpp_solver/tau_leaper.cpp +++ b/gillespy2/solvers/cpp/c_base/tau_leaping_cpp_solver/tau_leaper.cpp @@ -18,174 +18,6 @@ bool interrupted = false; void signalHandler(int signum){ interrupted = true; } -struct TauArgs{ - std::map HOR; - std::set reactants; - //Below are g_i_lambdas, pop element when used - std::map> g_i_lambdas; - std::map g_i; - std::map epsilon_i; - std::map> reactions_reactants; - std::map> products; - int critical_threshold = 10; -}; - -TauArgs initialize(Gillespy::Model &model, double tau_tol){ - - // Initialize TauArgs struct to be returned as a pointer - TauArgs tau_args; - // Initialize highest order rxns to 0 - for (int i=0; i0) - tau_args.products[r].push_back(spec); - else if (model.reactions[r].species_change[spec]<0){ - rxn_order +=1; - tau_args.reactions_reactants[r].push_back(spec); - tau_args.reactants.insert(model.species[spec]); - } - } - - // if this reaction's order is higher than previous, set - if (tau_args.reactions_reactants[r].size()>0){ - for (auto const &reactant:tau_args.reactions_reactants[r]){ - if (rxn_order > tau_args.HOR[model.species[reactant].name]){ - tau_args.HOR[model.species[reactant].name] = rxn_order; - tau_args.g_i[model.species[reactant].name] = tau_args.HOR[model.species[reactant].name]; - - int count = std::abs(model.reactions[r].species_change[reactant]); - if (count == 2 && rxn_order == 2){ - auto lambda = [](double x) {return (2 + (1 / (x - 1)));}; - tau_args.g_i_lambdas[model.species[reactant].name] = lambda; - } - else if(count == 2 && rxn_order == 3){ - auto lambda = [](double x) {return ((3 / 2) * (2 + (1 / (x - 1))));}; - tau_args.g_i_lambdas[model.species[reactant].name] = lambda; - } - else if (count == 3){ - auto lambda = [](double x) {return (3 + (1 / (x - 1)) + (2 / (x - 2)));}; - tau_args.g_i_lambdas[model.species[reactant].name] = lambda; - } - else{ - tau_args.g_i[model.species[reactant].name] = tau_args.HOR[model.species[reactant].name]; - tau_args.epsilon_i[model.species[reactant].name] = tau_tol / tau_args.g_i[model.species[reactant].name]; - } - - - } - } - } - } - - return tau_args; -} - -double select(Gillespy::Model &model, TauArgs &tau_args, const double &tau_tol, const double ¤t_time, const double &save_time, const std::vector &propensity_values, const std::vector ¤t_state){ - double tau; //tau time to step; - std::map critical_taus; //Mapping of possible critical_taus, to be evaluated - std::map mu_i; - std::map sigma_i; - bool critical = false; // system-wide flag, true when any reaction is critical - double non_critical_tau = 0; // holds the smallest tau time for non-critical reactions - double critical_tau = 0; // holds the smallest tau time for critical reactions - - int v;//used for number of population reactant consumes - - // initialize mu_i and sigma_i to 0 - for(int spec = 0; spec 0){ - critical = true; // Critical reaction present in simulation - } - int consumed = abs(model.reactions[reaction].species_change[reactant]); - mu_i[model.species[reactant].name] += consumed*propensity_values[reaction];//Cao, Gillespie, Petzold 32a - sigma_i[model.species[reactant].name] += std::pow(consumed,2) * propensity_values[reaction];//Cao, Gillespie, Petzold 32a - } - } - } - - // If a critical reaction is present, estimate tau for a single firing of each - // critical reaction with propensity > 0, and take the smallest tau - if (critical == true){ - for (int reaction = 0; reaction < model.number_reactions; reaction++){ - if (propensity_values[reaction]>0) - critical_taus[model.reactions[reaction].name] = 1/propensity_values[reaction]; - } - std::pair min; - //find min of critical_taus - min = *min_element(critical_taus.begin(), critical_taus.end(),[](const auto& lhs, const auto& rhs){ return lhs.second < rhs.second;}); - critical_tau = min.second; - } - - if (tau_args.g_i_lambdas.size()>0){ - for (auto const& x : tau_args.g_i_lambdas) - { - tau_args.g_i[x.first] = tau_args.g_i_lambdas[x.first](tau_args.g_i[x.first]); - tau_args.epsilon_i[x.first] = tau_tol / tau_args.g_i[x.first]; - tau_args.g_i_lambdas.erase(x.first); - } - } - - std::map tau_i; //Mapping of possible non-critical_taus, to be evaluated - - - for (const auto &r : tau_args.reactants) - { - double calculated_max = tau_args.epsilon_i[r.name] * current_state[r.id]; - double max_pop_change_mean = std::max(calculated_max, 1.0); - double max_pop_change_sd = pow(max_pop_change_mean,2); - if (mu_i[r.name] > 0){ // Cao, Gillespie, Petzold 33 - tau_i[r.name] = std::min(std::abs(max_pop_change_mean / mu_i[r.name]), max_pop_change_sd / sigma_i[r.name]); - } - } - - if (tau_i.size()>0){ - std::pair min; - //find min of tau_i - min = *min_element(tau_i.begin(), tau_i.end(),[](const auto& lhs, const auto& rhs){ return lhs.second < rhs.second;}); - non_critical_tau = min.second; - } - - // If all reactions are non-critical, use non-critical tau. - if (critical == false){ - tau = non_critical_tau; - } - // If all reactions are critical, use critical tau. - else if (tau_i.size()==0){ - tau = critical_tau; - } - // If there are both critical, and non critical reactions, - // Take the shortest tau between critica and non-critical. - else{ - tau = std::min(non_critical_tau, critical_tau); - } - // If selected tau exceeds save time, integrate to save time - if (tau > 0){ - tau = std::max(tau, 1e-10); - if (save_time - current_time > 0){ - tau = std::min(tau, save_time - current_time); - } - } - else{ - tau = save_time - current_time; - } - - return tau; -} std::pair,double> get_reactions(const Gillespy::Model *model, const std::vector &propensity_values, double tau_step, double current_time, double save_time){ /* @@ -216,7 +48,7 @@ void tau_leaper(Gillespy::Simulation* simulation, const double tau_tol){ signal(SIGINT, signalHandler); if(simulation){ //Initialize your tau args - TauArgs tau_args = initialize(*(simulation->model),tau_tol); + simulation->tau_args = initialize(*(simulation->model),tau_tol); double increment = simulation->timeline[1]-simulation->timeline[0]; @@ -266,7 +98,7 @@ void tau_leaper(Gillespy::Simulation* simulation, const double tau_tol){ } - tau_step = select(*(simulation->model), tau_args, tau_tol, simulation->current_time, save_time, propensity_values, current_state); // tau_step selection process + tau_step = select(*(simulation->model), simulation->tau_args, tau_tol, simulation->current_time, save_time, propensity_values, current_state); // tau_step selection process prev_curr_state = current_state; double prev_curr_time = simulation->current_time; @@ -286,17 +118,18 @@ void tau_leaper(Gillespy::Simulation* simulation, const double tau_tol){ std::map species_modified; for (int i = 0; imodel->number_reactions; i++){ if (rxn_count[simulation->model->reactions[i].name] > 0) - for (auto const &spec : tau_args.reactions_reactants[i]){ + for (auto const &spec : simulation->tau_args.reactions_reactants[i]){ species_modified[spec] = true; //+= for both reactants and products because current_state is represented with negative number changes for reactants, and positive for products. current_state[spec] += simulation->model->reactions[i].species_change[spec] * rxn_count[simulation->model->reactions[i].name]; } - for (auto const &spec : tau_args.products[i]){ - species_modified[spec] = true; - current_state[spec] += simulation->model->reactions[i].species_change[spec] * rxn_count[simulation->model->reactions[i].name]; - } - } + for (auto const &spec : simulation->tau_args.products[i]) + { + species_modified[spec] = true; + current_state[spec] += simulation->model->reactions[i].species_change[spec] * rxn_count[simulation->model->reactions[i].name]; + } + } bool neg_state = false; From 1cebaa0dddd627c89312ff81b39919c16098cd4b Mon Sep 17 00:00:00 2001 From: Joshua Cooper <42880781+jtcooper10@users.noreply.github.com> Date: Mon, 10 May 2021 18:45:24 -0400 Subject: [PATCH 35/88] Fix missing import in __init__.py `VariableSSACSolver` import mysteriously went missing during the merge from the `develop` branch; re-added --- gillespy2/solvers/cpp/__init__.py | 1 + 1 file changed, 1 insertion(+) diff --git a/gillespy2/solvers/cpp/__init__.py b/gillespy2/solvers/cpp/__init__.py index 9c5e74950..50c173482 100644 --- a/gillespy2/solvers/cpp/__init__.py +++ b/gillespy2/solvers/cpp/__init__.py @@ -1,6 +1,7 @@ from gillespy2.solvers.cpp.ssa_c_solver import SSACSolver from gillespy2.solvers.cpp.ode_c_solver import ODECSolver from gillespy2.solvers.cpp.tau_leaping_c_solver import TauLeapingCSolver +from gillespy2.solvers.cpp.variable_ssa_c_solver import VariableSSACSolver from gillespy2.solvers.cpp.tau_hybrid_c_solver import TauHybridCSolver # Call external function instead of implementing here so we don't need to rerun check on each model init. From afca4dda8749b3e4f06b6cd5c49cbd09e70f0831 Mon Sep 17 00:00:00 2001 From: Josh C Date: Fri, 14 May 2021 11:45:58 -0400 Subject: [PATCH 36/88] URN initialization for reaction states - Previous work on selection stuff is taped off - DO NOT DELETE the stuff behind the police tape! We'll be using it later --- gillespy2/solvers/cpp/__init__.py | 2 +- gillespy2/solvers/cpp/c_base/model.h | 28 ------ .../tau_hybrid_cpp_solver/HybridModel.h | 33 +++++++ .../TauHybridCSolver.cpp | 99 +++++++------------ 4 files changed, 71 insertions(+), 91 deletions(-) create mode 100644 gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/HybridModel.h diff --git a/gillespy2/solvers/cpp/__init__.py b/gillespy2/solvers/cpp/__init__.py index 50c173482..13d2e4b18 100644 --- a/gillespy2/solvers/cpp/__init__.py +++ b/gillespy2/solvers/cpp/__init__.py @@ -9,4 +9,4 @@ can_use_cpp = cpp_support __all__ = ['SSACSolver', 'VariableSSACSolver'] -__all__ = ['SSACSolver', 'ODECSolver', 'TauLeapingCSolver', 'VariableSSACSolver', 'TauHybridSolver'] +__all__ = ['SSACSolver', 'ODECSolver', 'TauLeapingCSolver', 'VariableSSACSolver', 'TauHybridCSolver'] diff --git a/gillespy2/solvers/cpp/c_base/model.h b/gillespy2/solvers/cpp/c_base/model.h index f03e02f8e..e33879e45 100644 --- a/gillespy2/solvers/cpp/c_base/model.h +++ b/gillespy2/solvers/cpp/c_base/model.h @@ -5,7 +5,6 @@ #include #include #include -#include "./Tau/tau.h" namespace Gillespy{ #define CONTINUOUS 0 @@ -19,32 +18,7 @@ namespace Gillespy{ //Used for hashing into set, for TauLeapingCSolver bool operator < (const Species &other) const { return id < other.id; } - - // Everything below here used for hybrid solver - // allows the user to specify if a species' population should definitely be modeled continuously or - // discretely - // CONTINUOUS or DISCRETE - // otherwise, mode will be determined by the program (DYNAMIC) - // if no choice is made, DYNAMIC will be assumed - int user_mode; - // during simulation execution, a species will fall into either of the two categories, CONTINUOUS or DISCRETE - // this is pre-determined only if the user_mode specifies CONTINUOUS or DISCRETE. - // otherwise, if DYNAMIC is specified, partition_mode will be continually calculated throughout the simulation - // according to standard deviation and coefficient of variance. - int partition_mode; - // Tolerance level for considering a dynamic species deterministically, value is compared - // to an estimated sd/mean population of a species after a given time step. - // This value will be used if a switch_min is not provided. The default value is 0.03 - double switch_tol = 0.03; - //Minimum population value at which species will be represented as continuous. - // If a value is given, switch_min will be used instead of switch_tol. - unsigned int switch_min = 0; }; - typedef union - { - int discrete; - double continuous; - } hybrid_state; struct Reaction{ unsigned int id; //useful for propensity function id associated @@ -89,8 +63,6 @@ namespace Gillespy{ double end_time; double current_time; int random_seed; - // for Tau-Leaping and Hybrid simulations - TauArgs tau_args; unsigned int number_timesteps; unsigned int number_trajectories; diff --git a/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/HybridModel.h b/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/HybridModel.h new file mode 100644 index 000000000..c3ea4e39b --- /dev/null +++ b/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/HybridModel.h @@ -0,0 +1,33 @@ +#include +#include "model.h" + +namespace Gillespy { + + struct HybridSpecies : Species + { + // allows the user to specify if a species' population should definitely be modeled continuously or + // discretely + // CONTINUOUS or DISCRETE + // otherwise, mode will be determined by the program (DYNAMIC) + // if no choice is made, DYNAMIC will be assumed + int user_mode; + // during simulation execution, a species will fall into either of the two categories, CONTINUOUS or DISCRETE + // this is pre-determined only if the user_mode specifies CONTINUOUS or DISCRETE. + // otherwise, if DYNAMIC is specified, partition_mode will be continually calculated throughout the simulation + // according to standard deviation and coefficient of variance. + int partition_mode; + // Tolerance level for considering a dynamic species deterministically, value is compared + // to an estimated sd/mean population of a species after a given time step. + // This value will be used if a switch_min is not provided. The default value is 0.03 + double switch_tol = 0.03; + //Minimum population value at which species will be represented as continuous. + // If a value is given, switch_min will be used instead of switch_tol. + unsigned int switch_min = 0; + }; + + union hybrid_state + { + unsigned int discrete; + double continuous; + }; +} diff --git a/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/TauHybridCSolver.cpp b/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/TauHybridCSolver.cpp index 67380e88b..eb9ba7da8 100644 --- a/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/TauHybridCSolver.cpp +++ b/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/TauHybridCSolver.cpp @@ -8,7 +8,7 @@ #include "sundials_types.h" // defs. of realtype, sunindextype #include "sundials_math.h" // contains the macros ABS, SUNSQR, EXP #include "TauHybridCSolver.h" -#include "model.h" +#include "HybridModel.h" #include "tau.h" using namespace Gillespy; @@ -39,6 +39,9 @@ namespace Gillespy { { interrupted = true; } + /************************************** + ***** POLICE TAPE : DO NOT CROSS ***** + **************************************//* void init_species_mode(const Model &model, Simulation &simulation){ int num_species = model.number_species; // int num_det_species = 0; @@ -130,7 +133,7 @@ namespace Gillespy { * Helper Function to get reactions fired from t to t+tau. Affects two values: * rxn_count - dict with key=Reaction channel value=number of times fired * curr_time - float representing current time - */ + *//* if (current_time + tau_step > save_time) tau_step = save_time - current_time; @@ -150,7 +153,9 @@ namespace Gillespy { values.second = current_time; return values; } - + /****************************** + ***** END OF POLICE TAPE ***** + ******************************/ @@ -167,12 +172,30 @@ namespace Gillespy { TauArgs tau_args = initialize(*(simulation->model),tau_tol); double increment = simulation->timeline[1] - simulation->timeline[0]; - + //initialize current_state vector to 0 for each species - std::vector current_state(num_species); + // TODO: change back double -> hybrid_state, once we figure out how that works + std::vector current_state(num_species); //initialize propensity_values to 0 for each species std::vector propensity_values(num_reactions); + // Hybrid solver is highly dependent on random numbers. + // In order to do this, a URN on the range [0,1) is generated. + // log( uniform(rng) ) returns a real number on the range (-inf, 0). + // TODO: either assign a seed or set seed to be configurable + std::mt19937_64 rng; + std::uniform_real_distribution uniform(0, 1); + + // Represents the current "randomized state" for each reaction, used as a + // helper value to determine if/how many stochastic reactions fire. + // This gets initialized to a random negative offset, and gets "less negative" + // during the integration step. + // After each integration step, the reaction_state is used to count stochastic reactions. + std::vector reaction_state(num_reactions); + for (int rxn_state = 0; rxn_state < num_reactions; ++rxn_state) { + reaction_state[rxn_state] = uniform(rng); + } + //copy initial state for each trajectory for(int s = 0; s < num_species; s++){ simulation->trajectories[0][0][s] = species[s].initial_population; @@ -184,66 +207,18 @@ namespace Gillespy { break; } - for (int s = 0; s < num_species; s++) { - if (species[s].user_mode == DISCRETE){ - current_state[s].discrete = species[s].initial_population; - }else { - current_state[s].continuous = (double) species[s].initial_population; - } + // Initialize the species population for the trajectory. + unsigned int spec_i; + for (spec_i = 0; spec_i < num_species; ++spec_i) { + current_state[spec_i] = species[spec_i].initial_population; } - simulation->current_time = 0; - //what is this? - int entry_count = 0; - //propensity sum is... - double propensity_sum; - //save time is... - double save_time = 0; - // steps rejected is... - int steps_rejected = 0; - //tau_step is... - double tau_step; - - std::vector prev_curr_state; - - // while (entry_count < simulation->number_timesteps) } } } } -static int f(realtype t, N_Vector y, N_Vector y_dot, void *user_data) { - // N_VGetArrayPointer returns a pointer to the data in the N_Vector class. - realtype *ydata = N_VGetArrayPointer(y); // pointer y vector - realtype *dydata = N_VGetArrayPointer(y_dot); // pointer ydot vec - UserData *sim_data; - sim_data = (UserData*) user_data; // void pointer magic - - std::vector curr_state; // create vector of curr_state doubles, sent to propensity_function->ODEEvaluate() - int number_species = sim_data->my_sim->model->number_species; // for readability - int number_reacs = sim_data->my_sim->model->number_reactions; // for readability - std::vector propensity; // Vector of propensities of type 'realtypes' (doubles used in SUNDIALS), - - for (sunindextype i = 0; i < number_species; i++){ - dydata[i] = 0; // Initialize change in y to '0' - curr_state.push_back(ydata[i]); // add values found in our curr_state vector, 'ydata' to our curr_state vector - // This vector is used for our propensity_function method "evaluate", defined in abstract in 'model.h' - } - - for (sunindextype rxn = 0; rxn < number_reacs; rxn++){ - // Calculate propensity for each reaction, at current state - propensity.push_back((sim_data->my_sim)->propensity_function->ODEEvaluate((int)rxn, curr_state)); - - for (sunindextype spec = 0; spec < (sim_data->my_sim)->model->number_species; spec++){ - // if species is a product of this reaction, add propensity fxn - if ((sim_data->my_sim)->model->reactions[rxn].species_change[spec] > 0){ - dydata[spec] += propensity[rxn]; - } - // else if this species is reactant, subtract propensity fxn - else if ((sim_data->my_sim)->model->reactions[rxn].species_change[spec] < 0){ - dydata[spec] -= propensity[rxn]; - } - } - } - return(0); -} - +static int f(realtype t, N_Vector y, N_Vector ydot, void *user_data) +{ + // TODO: implement me! + return 0; +}; From 664d0540c66a6139e0feb32db212925f603eb3a5 Mon Sep 17 00:00:00 2001 From: Josh C Date: Fri, 14 May 2021 13:26:59 -0400 Subject: [PATCH 37/88] Post-integration stochastic update - Assumes Tau has been selected and integration step has finished - To be implemented! - Counts stochastic firings for Tau step --- .../TauHybridCSolver.cpp | 75 +++++++++++++++++-- 1 file changed, 69 insertions(+), 6 deletions(-) diff --git a/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/TauHybridCSolver.cpp b/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/TauHybridCSolver.cpp index eb9ba7da8..9f2d286ac 100644 --- a/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/TauHybridCSolver.cpp +++ b/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/TauHybridCSolver.cpp @@ -18,6 +18,7 @@ static int f(realtype t, N_Vector y, N_Vector y_dot, void *user_data); // forwar struct UserData { Gillespy::Simulation *my_sim; + std::vector reaction_states; }; struct IntegratorOptions{ // CVODE constants returned if bad output, or success output. @@ -173,9 +174,9 @@ namespace Gillespy { double increment = simulation->timeline[1] - simulation->timeline[0]; - //initialize current_state vector to 0 for each species - // TODO: change back double -> hybrid_state, once we figure out how that works - std::vector current_state(num_species); + // Population/concentration state values for each species. + // TODO: change back int -> hybrid_state, once we figure out how that works + std::vector current_state(num_species); //initialize propensity_values to 0 for each species std::vector propensity_values(num_reactions); @@ -199,19 +200,81 @@ namespace Gillespy { //copy initial state for each trajectory for(int s = 0; s < num_species; s++){ simulation->trajectories[0][0][s] = species[s].initial_population; + current_state[s] = species[s].initial_population; } //Simulate for each trajectory - //make new method here for(int traj = 0; traj < num_trajectories; traj++){ if (interrupted){ break; } // Initialize the species population for the trajectory. - unsigned int spec_i; - for (spec_i = 0; spec_i < num_species; ++spec_i) { + for (int spec_i = 0; spec_i < num_species; ++spec_i) { current_state[spec_i] = species[spec_i].initial_population; } + + // SIMULATION STEP LOOP + double next_time; + double tau_step; + while (simulation->current_time < simulation->end_time) { + // INTEGRATE() + // TODO: implement me! + // For deterministic reactions, the concentrations are updated directly. + // For stochastic reactions, integration updates the reaction_states vector. + tau_step = increment; + + // Determine what the next time point is. + // This will become current_time on the next iteration. + // If a retry with a smaller tau_step is deemed necessary, this will change. + next_time = simulation->current_time + tau_step; + + // The newly-updated reaction_states vector may need to be reconciled now. + // A positive reaction_state means reactions have potentially fired. + // NOTE: it is possible for a population to swing negative, where a smaller Tau is needed. + for (int rxn_i = 0; rxn_i < num_reactions; ++rxn_i) { + // Temporary variable for the reaction's state. + // Does not get updated unless the changes are deemed valid. + double rxn_state = reaction_state[rxn_i]; + + // Temporary array to store changes to dependent species, 0-initialized. + unsigned int population_changes[num_species]; + for (int p_i = 0; p_i < num_species; ++p_i) + population_changes[p_i] = 0; + + // Use the current reaction_state to count the number of firings. + // If a negative population is detected, then the loop breaks prematurely. + while (rxn_state > 0) { + // "Fire" a reaction by recording changes in dependent species. + // If a negative value is detected, break without saving changes. + for (int spec_i = 0; spec_i < num_species; ++spec_i) { + population_changes[spec_i] += model.reactions[rxn_i].species_change[spec_i]; + if (population_changes[spec_i] < 0) { + break; + } + } + + // uniform(rng) is a random number on range (0,1), always fractional + // This means that log(uniform(rng)) is always negative + rxn_state += log(uniform(rng)); + } + + // Positive reaction state means a negative population was detected. + // Only update state with the given population changes if valid. + if (rxn_state <= 0) { + for (int p_i = 0; p_i < num_species; ++p_i) { + current_state[p_i] += population_changes[p_i]; + } + reaction_state[rxn_i] = rxn_state; + } + else { + // Invalid population state detected; try a smaller Tau step. + next_time = simulation->current_time; + tau_step *= 0.5; + } + } + + simulation->current_time = next_time; + } } } } From b138f6e3da9735b9a1a01d76aa1b051e2c708484 Mon Sep 17 00:00:00 2001 From: Josh C Date: Fri, 14 May 2021 19:48:19 -0400 Subject: [PATCH 38/88] Pure ODE implementation of hybrid solver - Adds RHS of integrator - Setup for ODE solver state - Not yet tested! Template needs some finagling --- .../tau_hybrid_cpp_solver/HybridModel.h | 3 + .../TauHybridCSolver.cpp | 107 +++++++++++++++--- .../TauHybridTemplate.cpp | 1 - 3 files changed, 97 insertions(+), 14 deletions(-) diff --git a/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/HybridModel.h b/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/HybridModel.h index c3ea4e39b..fd1cea4f1 100644 --- a/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/HybridModel.h +++ b/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/HybridModel.h @@ -3,6 +3,9 @@ namespace Gillespy { + #define GPY_HYBRID_ABSTOL 1e-5 + #define GPY_HYBRID_RELTOL 1e-5 + struct HybridSpecies : Species { // allows the user to specify if a species' population should definitely be modeled continuously or diff --git a/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/TauHybridCSolver.cpp b/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/TauHybridCSolver.cpp index 9f2d286ac..9df4c9e4f 100644 --- a/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/TauHybridCSolver.cpp +++ b/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/TauHybridCSolver.cpp @@ -19,6 +19,7 @@ static int f(realtype t, N_Vector y, N_Vector y_dot, void *user_data); // forwar struct UserData { Gillespy::Simulation *my_sim; std::vector reaction_states; + std::vector concentrations; }; struct IntegratorOptions{ // CVODE constants returned if bad output, or success output. @@ -170,13 +171,13 @@ namespace Gillespy { int num_trajectories = simulation->number_trajectories; Model &model = *(simulation->model); std::unique_ptr &species = model.species; - TauArgs tau_args = initialize(*(simulation->model),tau_tol); + //TauArgs tau_args = initialize(*(simulation->model),tau_tol); double increment = simulation->timeline[1] - simulation->timeline[0]; // Population/concentration state values for each species. - // TODO: change back int -> hybrid_state, once we figure out how that works - std::vector current_state(num_species); + // TODO: change back double -> hybrid_state, once we figure out how that works + std::vector current_state(num_species); //initialize propensity_values to 0 for each species std::vector propensity_values(num_reactions); @@ -213,25 +214,52 @@ namespace Gillespy { current_state[spec_i] = species[spec_i].initial_population; } + // Struct acts as container for simulation state. + // This gets passed in to the integrator. + UserData *data = new UserData { + simulation, + reaction_state, + current_state + }; + + // Initialize integrator state. + N_Vector y0 = N_VNew_Serial(num_species); + for (int spec_i = 0; spec_i < num_species; ++spec_i) { + NV_Ith_S(y0, spec_i) = species[spec_i].initial_population; + } + + // Build the ODE memory object and initialize it. + // Accepts initial integrator state y0, start time t0, and RHS f. + void *cvode_mem = CVodeCreate(CV_BDF); + realtype t0 = 0; + int flag = 0; + flag = CVodeInit(cvode_mem, f, t0, y0); + flag = CVodeSStolerances(cvode_mem, GPY_HYBRID_RELTOL, GPY_HYBRID_ABSTOL); + + // Build the Linear Solver object and initialize it. + SUNLinearSolver LS = SUNLinSol_SPGMR(y0, 0, 0); + flag = CVodeSetUserData(cvode_mem, data); + flag = CVodeSetLinearSolver(cvode_mem, LS, NULL); + // SIMULATION STEP LOOP double next_time; - double tau_step; + double tau_step = increment; + int save_time = 0; while (simulation->current_time < simulation->end_time) { - // INTEGRATE() - // TODO: implement me! - // For deterministic reactions, the concentrations are updated directly. - // For stochastic reactions, integration updates the reaction_states vector. - tau_step = increment; - // Determine what the next time point is. // This will become current_time on the next iteration. // If a retry with a smaller tau_step is deemed necessary, this will change. next_time = simulation->current_time + tau_step; + // Integration Step + // For deterministic reactions, the concentrations are updated directly. + // For stochastic reactions, integration updates the reaction_states vector. + flag = CVode(cvode_mem, next_time, y0, &next_time, CV_NORMAL); + // The newly-updated reaction_states vector may need to be reconciled now. // A positive reaction_state means reactions have potentially fired. // NOTE: it is possible for a population to swing negative, where a smaller Tau is needed. - for (int rxn_i = 0; rxn_i < num_reactions; ++rxn_i) { + for (int rxn_i = 0; false && rxn_i < num_reactions; ++rxn_i) { // Temporary variable for the reaction's state. // Does not get updated unless the changes are deemed valid. double rxn_state = reaction_state[rxn_i]; @@ -270,18 +298,71 @@ namespace Gillespy { // Invalid population state detected; try a smaller Tau step. next_time = simulation->current_time; tau_step *= 0.5; + + // TODO: Reset the integrator state to the previous time step. } } + // Output the results for this time step. simulation->current_time = next_time; + + while (save_time < next_time) { + // Write each species, one at a time (from ODE solution) + for (int spec_i; spec_i < num_species; ++spec_i) { + simulation->trajectoriesODE[traj][save_time][spec_i] = NV_Ith_S(y0, spec_i); + } + save_time += increment; + } + } + + // End of trajectory + // Clean up integrator data structures + N_VDestroy_Serial(y0); + CVodeFree(&cvode_mem); + SUNLinSolFree_SPGMR(LS); + delete data; } } } } -static int f(realtype t, N_Vector y, N_Vector ydot, void *user_data) +static int f(realtype t, N_Vector y, N_Vector f, void *user_data) { - // TODO: implement me! + // Get y(t) vector and f(t, y) vector + realtype *Y = N_VGetArrayPointer(y); + realtype *dydt = N_VGetArrayPointer(f); + realtype propensity; + + // Extract simulation data + UserData *data = static_cast(user_data); + Simulation *sim = data->my_sim; + std::vector reaction_states = data->reaction_states; + std::vector concentrations = data->concentrations; + + // Populate the current ODE state into the concentrations vector. + unsigned int spec_i; + for (spec_i = 0; spec_i < sim->model->number_species; ++spec_i) { + concentrations[spec_i] = Y[spec_i]; + } + + // Each species has a "spot" in the y and f(y,t) vector. + // For each species, place the result of f(y,t) into Yout vector. + unsigned int rxn_i; + for (rxn_i = 0; rxn_i < sim->model->number_reactions; ++rxn_i) { + propensity = sim->propensity_function->ODEEvaluate(rxn_i, concentrations); + + for (spec_i = 0; spec_i < sim->model->number_species; ++spec_i) { + if (sim->model->reactions[rxn_i].species_change[spec_i] == 0) + continue; + // Use the evaluated propensity to update the concentration levels and reaction state. + // Propensity is treated as positive if it's a product, negative if it's a reactant. + concentrations[spec_i] += propensity * ( + sim->model->reactions[rxn_i].species_change[spec_i] > 0 + ? 1 : -1 + ); + } + } + return 0; }; diff --git a/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/TauHybridTemplate.cpp b/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/TauHybridTemplate.cpp index e8e65ea35..8c779e169 100644 --- a/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/TauHybridTemplate.cpp +++ b/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/TauHybridTemplate.cpp @@ -4,7 +4,6 @@ #include #include #include -#include "model.h" #include "TauHybridCSolver.h" using namespace Gillespy; From b5bb180b896f3e9330d303760e5fe92a85b43938 Mon Sep 17 00:00:00 2001 From: Josh C Date: Tue, 18 May 2021 09:51:28 -0400 Subject: [PATCH 39/88] TauHybridCSolver runnable - Doesn't actually work, but it can now be tested from Python - Does not yet actually write out results --- gillespy2/solvers/cpp/c_base/model.cpp | 13 +++++++------ .../tau_hybrid_cpp_solver/TauHybridCSolver.cpp | 2 +- .../c_base/tau_hybrid_cpp_solver/TauHybridCSolver.h | 2 +- .../tau_hybrid_cpp_solver/TauHybridTemplate.cpp | 2 +- .../cpp/c_base/tau_hybrid_cpp_solver/makefile | 10 +++++----- 5 files changed, 15 insertions(+), 14 deletions(-) diff --git a/gillespy2/solvers/cpp/c_base/model.cpp b/gillespy2/solvers/cpp/c_base/model.cpp index 6ffa954f7..2519b79d3 100644 --- a/gillespy2/solvers/cpp/c_base/model.cpp +++ b/gillespy2/solvers/cpp/c_base/model.cpp @@ -133,12 +133,13 @@ namespace Gillespy{ os<trajectories[0][0][s] = species[s].initial_population; + simulation->trajectoriesODE[0][0][s] = species[s].initial_population; current_state[s] = species[s].initial_population; } //Simulate for each trajectory diff --git a/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/TauHybridCSolver.h b/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/TauHybridCSolver.h index dc81635f1..7af3b5a61 100644 --- a/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/TauHybridCSolver.h +++ b/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/TauHybridCSolver.h @@ -3,7 +3,7 @@ #include "model.h" namespace Gillespy { - void TauHybridSolver(Gillespy::Simulation* simulation, const double tau_tol); + void TauHybridCSolver(Gillespy::Simulation* simulation, const double tau_tol); } #endif \ No newline at end of file diff --git a/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/TauHybridTemplate.cpp b/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/TauHybridTemplate.cpp index 8c779e169..e76dc935e 100644 --- a/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/TauHybridTemplate.cpp +++ b/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/TauHybridTemplate.cpp @@ -91,7 +91,7 @@ __DEFINE_REACTIONS_ simulation.propensity_function = propFun; simulationINIT(&model, simulation); // Perform ODE // - // TauHybridCSolver(&simulation,); + TauHybridCSolver(&simulation, 0.05); simulation.output_results_buffer(std :: cout); delete propFun; return 0; diff --git a/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/makefile b/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/makefile index 07f93792b..4ce7dcd88 100644 --- a/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/makefile +++ b/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/makefile @@ -18,7 +18,7 @@ SUNOBJ = cvode_nls.o cvode_io.o sundials_iterative.o cvode_proj.o sundials_matri sundials_linearsolver.o sundials_nonlinearsolver.o sundials_nvector_senswrapper.o sunnonlinsol_newton.o \ sundials_nvector.o nvector_serial.o cvode.o cvode_spils.o sundials_math.o sunlinsol_spgmr.o -# all: model.o TauHybridSimulation.o TauHybridCSolver.o TauHybridSimulation +all: model.o TauHybridCSolver.o TauHybridSimulation.o TauHybridSimulation model.o: $(CC) -c -o model.o $(CBASE_DIR)/model.cpp $(CFLAGS) -I$(CBASE_DIR) -I$(INCDIR) @@ -26,13 +26,13 @@ model.o: %.o: $(SRCDIR)/%.c $(CC) -c -o $@ $< $(CFLAGS) -I$(INCDIR) -TauHybridSimulation.o: - $(CC) -c -o TauHybridSimulation.o TauHybridSimulation.cpp $(CFLAGS) -I$(CBASE_DIR) -I$(GILLESPY_CPP_TAU_HYBRID_DIR) - TauHybridCSolver.o: $(CC) -c -o TauHybridCSolver.o $(GILLESPY_CPP_TAU_HYBRID_DIR)/TauHybridCSolver.cpp $(CFLAGS) -I$(CBASE_DIR) -I$(SRCDIR) -I$(INCDIR) -I$(CBASE_DIR)/Tau -TauHybridSimulation: TauHybridCSolver.o TauHybridSimulation.o $(SUNOBJ) model.o +TauHybridSimulation.o: + $(CC) -c -o TauHybridSimulation.o TauHybridSimulation.cpp $(CFLAGS) -I$(CBASE_DIR) -I$(GILLESPY_CPP_TAU_HYBRID_DIR) + +TauHybridSimulation: TauHybridSimulation.o TauHybridCSolver.o $(SUNOBJ) model.o $(CC) -o TauHybridSimulation TauHybridCSolver.o TauHybridSimulation.o $(SUNOBJ) model.o $(LINKFLAGS) cleanSimulationODE: From bc3f8a6728b24f3a8b6083e5784786d894cf6e50 Mon Sep 17 00:00:00 2001 From: Josh C Date: Tue, 18 May 2021 11:27:31 -0400 Subject: [PATCH 40/88] Pure ODE solution for hybrid solver - Not yet configurable; assumes ODE - Writes results out to ODE result vector --- .../TauHybridCSolver.cpp | 24 +++++++++++-------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/TauHybridCSolver.cpp b/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/TauHybridCSolver.cpp index 965aaa1bf..3a91fbce4 100644 --- a/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/TauHybridCSolver.cpp +++ b/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/TauHybridCSolver.cpp @@ -19,7 +19,6 @@ static int f(realtype t, N_Vector y, N_Vector y_dot, void *user_data); // forwar struct UserData { Gillespy::Simulation *my_sim; std::vector reaction_states; - std::vector concentrations; }; struct IntegratorOptions{ // CVODE constants returned if bad output, or success output. @@ -218,8 +217,7 @@ namespace Gillespy { // This gets passed in to the integrator. UserData *data = new UserData { simulation, - reaction_state, - current_state + reaction_state }; // Initialize integrator state. @@ -306,9 +304,9 @@ namespace Gillespy { // Output the results for this time step. simulation->current_time = next_time; - while (save_time < next_time) { + while (save_time <= next_time) { // Write each species, one at a time (from ODE solution) - for (int spec_i; spec_i < num_species; ++spec_i) { + for (int spec_i = 0; spec_i < num_species; ++spec_i) { simulation->trajectoriesODE[traj][save_time][spec_i] = NV_Ith_S(y0, spec_i); } save_time += increment; @@ -327,27 +325,33 @@ namespace Gillespy { } } -static int f(realtype t, N_Vector y, N_Vector f, void *user_data) +/** + * Integrator function for ODE linear solver. + * This gets passed directly to the Sundials ODE solver once initialized. + */ +static int f(realtype t, N_Vector y, N_Vector ydot, void *user_data) { // Get y(t) vector and f(t, y) vector realtype *Y = N_VGetArrayPointer(y); - realtype *dydt = N_VGetArrayPointer(f); + realtype *dydt = N_VGetArrayPointer(ydot); realtype propensity; // Extract simulation data UserData *data = static_cast(user_data); Simulation *sim = data->my_sim; std::vector reaction_states = data->reaction_states; - std::vector concentrations = data->concentrations; + std::vector concentrations(sim->model->number_species); // Populate the current ODE state into the concentrations vector. + // dy/dt results are initialized to zero, and become the change in propensity. unsigned int spec_i; for (spec_i = 0; spec_i < sim->model->number_species; ++spec_i) { concentrations[spec_i] = Y[spec_i]; + dydt[spec_i] = 0; } // Each species has a "spot" in the y and f(y,t) vector. - // For each species, place the result of f(y,t) into Yout vector. + // For each species, place the result of f(y,t) into dydt vector. unsigned int rxn_i; for (rxn_i = 0; rxn_i < sim->model->number_reactions; ++rxn_i) { propensity = sim->propensity_function->ODEEvaluate(rxn_i, concentrations); @@ -357,7 +361,7 @@ static int f(realtype t, N_Vector y, N_Vector f, void *user_data) continue; // Use the evaluated propensity to update the concentration levels and reaction state. // Propensity is treated as positive if it's a product, negative if it's a reactant. - concentrations[spec_i] += propensity * ( + dydt[spec_i] += propensity * ( sim->model->reactions[rxn_i].species_change[spec_i] > 0 ? 1 : -1 ); From 4afce39a8061840c8221c8f446dee6a047798787 Mon Sep 17 00:00:00 2001 From: Josh C Date: Tue, 18 May 2021 11:44:23 -0400 Subject: [PATCH 41/88] Branchless propensity update in integrator - Replaces ternary with branchless alternative - Saw performance gains of ~20%!!! --- .../tau_hybrid_cpp_solver/TauHybridCSolver.cpp | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/TauHybridCSolver.cpp b/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/TauHybridCSolver.cpp index 3a91fbce4..9344db59d 100644 --- a/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/TauHybridCSolver.cpp +++ b/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/TauHybridCSolver.cpp @@ -353,18 +353,22 @@ static int f(realtype t, N_Vector y, N_Vector ydot, void *user_data) // Each species has a "spot" in the y and f(y,t) vector. // For each species, place the result of f(y,t) into dydt vector. unsigned int rxn_i; + int species_change; for (rxn_i = 0; rxn_i < sim->model->number_reactions; ++rxn_i) { propensity = sim->propensity_function->ODEEvaluate(rxn_i, concentrations); for (spec_i = 0; spec_i < sim->model->number_species; ++spec_i) { - if (sim->model->reactions[rxn_i].species_change[spec_i] == 0) - continue; // Use the evaluated propensity to update the concentration levels and reaction state. // Propensity is treated as positive if it's a product, negative if it's a reactant. - dydt[spec_i] += propensity * ( - sim->model->reactions[rxn_i].species_change[spec_i] > 0 - ? 1 : -1 - ); + species_change = sim->model->reactions[rxn_i].species_change[spec_i]; + if (species_change == 0) + continue; + + // The product on the right evaluates to 1 if species_change is positive, + // and -1 if it's negative. + // This is a branchless alternative to using an if-statement. + // Saw a performance gain of ~20% by using this branchless method. + dydt[spec_i] += propensity * (-1 + 2 * (species_change > 0)); } } From fc8699bb569c7231345eb55e1c990ab10c226fee Mon Sep 17 00:00:00 2001 From: Josh C Date: Thu, 20 May 2021 12:25:57 -0400 Subject: [PATCH 42/88] Add reaction offsets to integration vector - Remove `reaction_states` vector - Reserve portion of integrator state vector for reaction states --- .../TauHybridCSolver.cpp | 82 +++++++++++++------ 1 file changed, 58 insertions(+), 24 deletions(-) diff --git a/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/TauHybridCSolver.cpp b/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/TauHybridCSolver.cpp index 9344db59d..62750f7fe 100644 --- a/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/TauHybridCSolver.cpp +++ b/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/TauHybridCSolver.cpp @@ -18,7 +18,6 @@ static int f(realtype t, N_Vector y, N_Vector y_dot, void *user_data); // forwar struct UserData { Gillespy::Simulation *my_sim; - std::vector reaction_states; }; struct IntegratorOptions{ // CVODE constants returned if bad output, or success output. @@ -187,16 +186,6 @@ namespace Gillespy { std::mt19937_64 rng; std::uniform_real_distribution uniform(0, 1); - // Represents the current "randomized state" for each reaction, used as a - // helper value to determine if/how many stochastic reactions fire. - // This gets initialized to a random negative offset, and gets "less negative" - // during the integration step. - // After each integration step, the reaction_state is used to count stochastic reactions. - std::vector reaction_state(num_reactions); - for (int rxn_state = 0; rxn_state < num_reactions; ++rxn_state) { - reaction_state[rxn_state] = uniform(rng); - } - //copy initial state for each trajectory for(int s = 0; s < num_species; s++){ simulation->trajectoriesODE[0][0][s] = species[s].initial_population; @@ -217,15 +206,35 @@ namespace Gillespy { // This gets passed in to the integrator. UserData *data = new UserData { simulation, - reaction_state }; - // Initialize integrator state. + // INITIALIZE INTEGRATOR STATE + // Integrator is used to integrate two variable sets separately: + // - concentrations for deterministic reactions + // - reaction offsets for stochastic reactions + // [ --- concentrations --- | --- rxn_offsets --- ] + // concentrations: bounded by [0, num_species) + // rxn_offsets: bounded by [num_species, num_species + num_reactions) + int rxn_offset_boundary = num_species + num_reactions; + + // The first half of the integration vector is used for integrating species concentrations. + // [ --- concentrations --- | ... N_Vector y0 = N_VNew_Serial(num_species); for (int spec_i = 0; spec_i < num_species; ++spec_i) { NV_Ith_S(y0, spec_i) = species[spec_i].initial_population; } + // The second half represents the current "randomized state" for each reaction. + // ... | --- rxn_offsets --- ] + for (int rxn_i = num_species; rxn_i < rxn_offset_boundary; ++rxn_i) { + // Represents the current "randomized state" for each reaction, used as a + // helper value to determine if/how many stochastic reactions fire. + // This gets initialized to a random negative offset, and gets "less negative" + // during the integration step. + // After each integration step, the reaction_state is used to count stochastic reactions. + NV_Ith_S(y0, rxn_i) = log(uniform(rng)); + } + // Build the ODE memory object and initialize it. // Accepts initial integrator state y0, start time t0, and RHS f. void *cvode_mem = CVodeCreate(CV_BDF); @@ -243,6 +252,10 @@ namespace Gillespy { double next_time; double tau_step = increment; int save_time = 0; + + // Temporary array to store changes to dependent species. + // Should be 0-initialized each time it's used. + int *population_changes = new int[num_species]; while (simulation->current_time < simulation->end_time) { // Determine what the next time point is. // This will become current_time on the next iteration. @@ -251,30 +264,36 @@ namespace Gillespy { // Integration Step // For deterministic reactions, the concentrations are updated directly. - // For stochastic reactions, integration updates the reaction_states vector. + // For stochastic reactions, integration updates the rxn_offsets vector. flag = CVode(cvode_mem, next_time, y0, &next_time, CV_NORMAL); + // "Extract" the different partitions of the vector. + // [ --- concentrations --- | --- rxn_offsets --- ] + // concentrations: bounded by [0, num_species) + // rxn_offsets: bounded by [num_species, num_species + num_reactions) + realtype *concentrations = NV_DATA_S(y0); + realtype *rxn_offsets = NV_DATA_S(y0) + num_species; + // The newly-updated reaction_states vector may need to be reconciled now. // A positive reaction_state means reactions have potentially fired. // NOTE: it is possible for a population to swing negative, where a smaller Tau is needed. - for (int rxn_i = 0; false && rxn_i < num_reactions; ++rxn_i) { + for (int rxn_i = num_species; false && rxn_i < rxn_offset_boundary; ++rxn_i) { // Temporary variable for the reaction's state. // Does not get updated unless the changes are deemed valid. - double rxn_state = reaction_state[rxn_i]; + double rxn_state = rxn_offsets[rxn_i]; - // Temporary array to store changes to dependent species, 0-initialized. - unsigned int population_changes[num_species]; + // 0-initialize our population_changes array. for (int p_i = 0; p_i < num_species; ++p_i) population_changes[p_i] = 0; // Use the current reaction_state to count the number of firings. // If a negative population is detected, then the loop breaks prematurely. - while (rxn_state > 0) { + while (rxn_state >= 0) { // "Fire" a reaction by recording changes in dependent species. // If a negative value is detected, break without saving changes. for (int spec_i = 0; spec_i < num_species; ++spec_i) { population_changes[spec_i] += model.reactions[rxn_i].species_change[spec_i]; - if (population_changes[spec_i] < 0) { + if (current_state[spec_i] + population_changes[spec_i] < 0) { break; } } @@ -286,11 +305,13 @@ namespace Gillespy { // Positive reaction state means a negative population was detected. // Only update state with the given population changes if valid. - if (rxn_state <= 0) { + if (rxn_state < 0) { for (int p_i = 0; p_i < num_species; ++p_i) { current_state[p_i] += population_changes[p_i]; } - reaction_state[rxn_i] = rxn_state; + + // "Permanently" update the rxn_state in the integrator. + rxn_offsets[rxn_i] = rxn_state; } else { // Invalid population state detected; try a smaller Tau step. @@ -320,6 +341,7 @@ namespace Gillespy { CVodeFree(&cvode_mem); SUNLinSolFree_SPGMR(LS); delete data; + delete[] population_changes; } } } @@ -339,7 +361,11 @@ static int f(realtype t, N_Vector y, N_Vector ydot, void *user_data) // Extract simulation data UserData *data = static_cast(user_data); Simulation *sim = data->my_sim; - std::vector reaction_states = data->reaction_states; + unsigned int num_species = sim->model->number_species; + unsigned int num_reactions = sim->model->number_reactions; + + realtype *rxn_offsets = &Y[num_species]; + int rxn_offset_boundary = num_species + num_reactions; std::vector concentrations(sim->model->number_species); // Populate the current ODE state into the concentrations vector. @@ -353,10 +379,19 @@ static int f(realtype t, N_Vector y, N_Vector ydot, void *user_data) // Each species has a "spot" in the y and f(y,t) vector. // For each species, place the result of f(y,t) into dydt vector. unsigned int rxn_i; + unsigned int rxn_offset_i; int species_change; for (rxn_i = 0; rxn_i < sim->model->number_reactions; ++rxn_i) { + // Index to the reaction's offset state in the integrator. + rxn_offset_i = rxn_i + num_species; + + // NOTE: we may need to evaluate ODE and Tau propensities separately. + // At the moment, it's unsure whether or not that's required. propensity = sim->propensity_function->ODEEvaluate(rxn_i, concentrations); + // Integrate this reaction's rxn_offset forward using the propensity. + rxn_offsets[rxn_offset_i] = Y[rxn_offset_i] + propensity; + for (spec_i = 0; spec_i < sim->model->number_species; ++spec_i) { // Use the evaluated propensity to update the concentration levels and reaction state. // Propensity is treated as positive if it's a product, negative if it's a reactant. @@ -367,7 +402,6 @@ static int f(realtype t, N_Vector y, N_Vector ydot, void *user_data) // The product on the right evaluates to 1 if species_change is positive, // and -1 if it's negative. // This is a branchless alternative to using an if-statement. - // Saw a performance gain of ~20% by using this branchless method. dydt[spec_i] += propensity * (-1 + 2 * (species_change > 0)); } } From 713f2ea9f57749c4d5686249489b4f0202e5a2b1 Mon Sep 17 00:00:00 2001 From: Josh C Date: Tue, 1 Jun 2021 13:39:20 -0400 Subject: [PATCH 43/88] Move solver statistics helpers to a separate file - `statistics.cpp` and `statistics.h` added - New namespaces, `Gillespy::TauHybrid` and `Gillespy::TauHybrid::Statistics` --- .../TauHybridCSolver.cpp | 122 +----------------- .../tau_hybrid_cpp_solver/statistics.cpp | 121 +++++++++++++++++ .../c_base/tau_hybrid_cpp_solver/statistics.h | 29 +++++ 3 files changed, 152 insertions(+), 120 deletions(-) create mode 100644 gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/statistics.cpp create mode 100644 gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/statistics.h diff --git a/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/TauHybridCSolver.cpp b/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/TauHybridCSolver.cpp index 62750f7fe..e1ea463fc 100644 --- a/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/TauHybridCSolver.cpp +++ b/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/TauHybridCSolver.cpp @@ -9,6 +9,7 @@ #include "sundials_math.h" // contains the macros ABS, SUNSQR, EXP #include "TauHybridCSolver.h" #include "HybridModel.h" +#include "statistics.h" #include "tau.h" using namespace Gillespy; @@ -32,132 +33,13 @@ struct IntegratorOptions{ realtype reltol; // double max_step; }; -namespace Gillespy { +namespace Gillespy::TauHybrid { bool interrupted = false; void signalHandler(int signum) { interrupted = true; } - /************************************** - ***** POLICE TAPE : DO NOT CROSS ***** - **************************************//* - void init_species_mode(const Model &model, Simulation &simulation){ - int num_species = model.number_species; - // int num_det_species = 0; - for (int s = 0; s < num_species; s++){ - // if the user chooses discrete, initialise the partition flag to such. - if (model.species[s].user_mode == DISCRETE){ - model.species[s].partition_mode = DISCRETE; - } - // otherwise, either the user chose continuous or dynamic (or null). - // in any case, just initialise to continuous. - else { - model.species[s].partition_mode = CONTINUOUS; - // num_det_species++; - } - } - // simulation.number_det_species = num_det_species; - } - void partition_species(const Model &model, const std::vector &propensity_values, std::vector curr_state, double tau_step, double current_time, std::map &det_species){ - // coefficient of variance- key:species id, value: cv - std::map cv; - // means - std::map means; - // standard deviation - std::map sd; - //init means - for (int i = 0; i < model.number_species; ++i){ - if (model.species[i].user_mode == DYNAMIC){ - if (model.species[i].partition_mode == CONTINUOUS){ - means.insert({i, curr_state[i].continuous}); - }else { - means.insert({i, curr_state[i].discrete}); - } - } - } - //init sd's - for (int i = 0; i < model.number_species; ++i){ - if (model.species[i].user_mode == DYNAMIC){ - sd.insert({i, 0}); - } - } - // calculate means and standard deviations for dynamic-mode species involved in reactions - for (int r = 0; r < model.number_reactions; ++r){ - for (int s = 0; s < model.number_species; ++s){ - // access list of species by accessing the correct element of the state-change vector (of the reaction) - if (model.species[model.reactions[r].species_change[s]].user_mode == DYNAMIC ){ - // if less than 0, that means this is a reactant - if (model.reactions[r].species_change[s] < 0){ - means[s] -= (tau_step * propensity_values[r] * model.reactions[r].species_change[s]); - sd[s] += std::pow((tau_step * propensity_values[r] * model.reactions[r].species_change[s]),2); - } - // if greater than 0, that means this is a product - if (model.reactions[r].species_change[s] > 0){ - means[s] += (tau_step * propensity_values[r] * model.reactions[r].species_change[s]); - sd[s] += std::pow((tau_step * propensity_values[r] * model.reactions[r].species_change[s]),2); - } - } - } - } - // calculate coefficient of variation using means and sd - - for (int s = 0; s < model.number_species; ++s){ - if (means.count(s) > 0){ - Species sref = model.species[s]; - if (sref.switch_min == 0) { // (default value means switch min not set, use switch tol) - if (means[s] > 0){ - cv[s] = sd[s] / means[s]; - }else{ - cv[s] = 1; - } - if (cv[s] < sref.switch_tol){ - sref.partition_mode == CONTINUOUS; - }else{ - sref.partition_mode == DISCRETE; - } - }else{ - if (means[s] > sref.switch_min){ - sref.partition_mode == CONTINUOUS; - }else{ - sref.partition_mode == DISCRETE; - } - } - } - } - return //TODO; - } - std::pair, double> get_reactions(const Gillespy::Model *model, const std::vector &propensity_values, double tau_step, double current_time, double save_time) - { - /* - * Helper Function to get reactions fired from t to t+tau. Affects two values: - * rxn_count - dict with key=Reaction channel value=number of times fired - * curr_time - float representing current time - *//* - - if (current_time + tau_step > save_time) - tau_step = save_time - current_time; - - std::map rxn_count; // map of how many times reaction is fired - std::random_device rd; - std::mt19937 generator(rd()); - std::pair, double> values; // value pair to be returned, map of times {map of times reaction fired, current time} - - for (int i = 0; i < model->number_reactions; i++) - { - std::poisson_distribution poisson(propensity_values[i] * tau_step); - rxn_count[model->reactions[i].name] = poisson(generator); - } - current_time = current_time + tau_step; - values.first = rxn_count; - values.second = current_time; - return values; - } - /****************************** - ***** END OF POLICE TAPE ***** - ******************************/ - - void TauHybridCSolver(Gillespy::Simulation *simulation, const double tau_tol) { diff --git a/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/statistics.cpp b/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/statistics.cpp new file mode 100644 index 000000000..4af940d58 --- /dev/null +++ b/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/statistics.cpp @@ -0,0 +1,121 @@ +#include "statistics.h" +#include + +namespace Gillespy::TauHybrid::Statistics +{ + + void init_species_mode(const Model &model, Simulation &simulation){ + int num_species = model.number_species; + // int num_det_species = 0; + for (int s = 0; s < num_species; s++){ + // if the user chooses discrete, initialise the partition flag to such. + if (model.species[s].user_mode == DISCRETE){ + model.species[s].partition_mode = DISCRETE; + } + // otherwise, either the user chose continuous or dynamic (or null). + // in any case, just initialise to continuous. + else { + model.species[s].partition_mode = CONTINUOUS; + // num_det_species++; + } + } + // simulation.number_det_species = num_det_species; + } + + void partition_species(const Model &model, const std::vector &propensity_values, std::vector curr_state, double tau_step, double current_time, std::map &det_species){ + // coefficient of variance- key:species id, value: cv + std::map cv; + // means + std::map means; + // standard deviation + std::map sd; + //init means + for (int i = 0; i < model.number_species; ++i){ + if (model.species[i].user_mode == DYNAMIC){ + if (model.species[i].partition_mode == CONTINUOUS){ + means.insert({i, curr_state[i].continuous}); + }else { + means.insert({i, curr_state[i].discrete}); + } + } + } + //init sd's + for (int i = 0; i < model.number_species; ++i){ + if (model.species[i].user_mode == DYNAMIC){ + sd.insert({i, 0}); + } + } + // calculate means and standard deviations for dynamic-mode species involved in reactions + for (int r = 0; r < model.number_reactions; ++r){ + for (int s = 0; s < model.number_species; ++s){ + // access list of species by accessing the correct element of the state-change vector (of the reaction) + if (model.species[model.reactions[r].species_change[s]].user_mode == DYNAMIC ){ + // if less than 0, that means this is a reactant + if (model.reactions[r].species_change[s] < 0){ + means[s] -= (tau_step * propensity_values[r] * model.reactions[r].species_change[s]); + sd[s] += std::pow((tau_step * propensity_values[r] * model.reactions[r].species_change[s]),2); + } + // if greater than 0, that means this is a product + if (model.reactions[r].species_change[s] > 0){ + means[s] += (tau_step * propensity_values[r] * model.reactions[r].species_change[s]); + sd[s] += std::pow((tau_step * propensity_values[r] * model.reactions[r].species_change[s]),2); + } + } + } + } + // calculate coefficient of variation using means and sd + + for (int s = 0; s < model.number_species; ++s){ + if (means.count(s) > 0){ + Species sref = model.species[s]; + if (sref.switch_min == 0) { // (default value means switch min not set, use switch tol) + if (means[s] > 0){ + cv[s] = sd[s] / means[s]; + }else{ + cv[s] = 1; + } + if (cv[s] < sref.switch_tol){ + sref.partition_mode == CONTINUOUS; + }else{ + sref.partition_mode == DISCRETE; + } + }else{ + if (means[s] > sref.switch_min){ + sref.partition_mode == CONTINUOUS; + }else{ + sref.partition_mode == DISCRETE; + } + } + } + } + + return; // TODO + } + + + std::pair, double> get_reactions(const Gillespy::Model *model, const std::vector &propensity_values, double tau_step, double current_time, double save_time) + { + /* Helper Function to get reactions fired from t to t+tau. Affects two values: + * rxn_count - dict with key=Reaction channel value=number of times fired + * curr_time - float representing current time + */ + + if (current_time + tau_step > save_time) + tau_step = save_time - current_time; + + std::map rxn_count; // map of how many times reaction is fired + std::random_device rd; + std::mt19937 generator(rd()); + std::pair, double> values; // value pair to be returned, map of times {map of times reaction fired, current time} + + for (int i = 0; i < model->number_reactions; i++) + { + std::poisson_distribution poisson(propensity_values[i] * tau_step); + rxn_count[model->reactions[i].name] = poisson(generator); + } + current_time = current_time + tau_step; + values.first = rxn_count; + values.second = current_time; + return values; + } +} diff --git a/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/statistics.h b/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/statistics.h new file mode 100644 index 000000000..31d8465bd --- /dev/null +++ b/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/statistics.h @@ -0,0 +1,29 @@ +#pragma once + +#include "HybridModel.h" +#include +#include + +namespace Gillespy::TauHybrid::Statistics +{ + + void init_species_mode(const Model &model, Simulation &simulation); + + void partition_species( + const Model &model, + const std::vector &propensity_values, + std::vector current_state, + double tau_step, + double current_time, + std::map &det_species + ); + + std::pair, double> get_reactions( + const Model *model, + const std::vector &propensity_values, + double tau_step, + double current_time, + double save_time + ); + +} From 88f5ffd21a94461c15532f202b14902f808640f4 Mon Sep 17 00:00:00 2001 From: Matthew Dippel Date: Tue, 1 Jun 2021 13:46:45 -0400 Subject: [PATCH 44/88] draft of flag_det_rxns() --- .../TauHybridCSolver.cpp | 69 +-------- .../tau_hybrid_cpp_solver/hybridutils.c | 141 ++++++++++++++++++ .../tau_hybrid_cpp_solver/hybridutils.h | 0 3 files changed, 142 insertions(+), 68 deletions(-) create mode 100644 gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/hybridutils.c create mode 100644 gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/hybridutils.h diff --git a/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/TauHybridCSolver.cpp b/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/TauHybridCSolver.cpp index 62750f7fe..756017e08 100644 --- a/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/TauHybridCSolver.cpp +++ b/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/TauHybridCSolver.cpp @@ -59,74 +59,7 @@ namespace Gillespy { } // simulation.number_det_species = num_det_species; } - void partition_species(const Model &model, const std::vector &propensity_values, std::vector curr_state, double tau_step, double current_time, std::map &det_species){ - // coefficient of variance- key:species id, value: cv - std::map cv; - // means - std::map means; - // standard deviation - std::map sd; - //init means - for (int i = 0; i < model.number_species; ++i){ - if (model.species[i].user_mode == DYNAMIC){ - if (model.species[i].partition_mode == CONTINUOUS){ - means.insert({i, curr_state[i].continuous}); - }else { - means.insert({i, curr_state[i].discrete}); - } - } - } - //init sd's - for (int i = 0; i < model.number_species; ++i){ - if (model.species[i].user_mode == DYNAMIC){ - sd.insert({i, 0}); - } - } - // calculate means and standard deviations for dynamic-mode species involved in reactions - for (int r = 0; r < model.number_reactions; ++r){ - for (int s = 0; s < model.number_species; ++s){ - // access list of species by accessing the correct element of the state-change vector (of the reaction) - if (model.species[model.reactions[r].species_change[s]].user_mode == DYNAMIC ){ - // if less than 0, that means this is a reactant - if (model.reactions[r].species_change[s] < 0){ - means[s] -= (tau_step * propensity_values[r] * model.reactions[r].species_change[s]); - sd[s] += std::pow((tau_step * propensity_values[r] * model.reactions[r].species_change[s]),2); - } - // if greater than 0, that means this is a product - if (model.reactions[r].species_change[s] > 0){ - means[s] += (tau_step * propensity_values[r] * model.reactions[r].species_change[s]); - sd[s] += std::pow((tau_step * propensity_values[r] * model.reactions[r].species_change[s]),2); - } - } - } - } - // calculate coefficient of variation using means and sd - - for (int s = 0; s < model.number_species; ++s){ - if (means.count(s) > 0){ - Species sref = model.species[s]; - if (sref.switch_min == 0) { // (default value means switch min not set, use switch tol) - if (means[s] > 0){ - cv[s] = sd[s] / means[s]; - }else{ - cv[s] = 1; - } - if (cv[s] < sref.switch_tol){ - sref.partition_mode == CONTINUOUS; - }else{ - sref.partition_mode == DISCRETE; - } - }else{ - if (means[s] > sref.switch_min){ - sref.partition_mode == CONTINUOUS; - }else{ - sref.partition_mode == DISCRETE; - } - } - } - } - return //TODO; - } + std::pair, double> get_reactions(const Gillespy::Model *model, const std::vector &propensity_values, double tau_step, double current_time, double save_time) { /* diff --git a/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/hybridutils.c b/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/hybridutils.c new file mode 100644 index 000000000..b856dea00 --- /dev/null +++ b/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/hybridutils.c @@ -0,0 +1,141 @@ +#include +#include "TauHybridCSolver.h" +#include "HybridModel.h" +#include "tau.h" +#include "model.h" + +// toggle_reactions() +namespace Gillespy { + void create_diff_eqs() + { + } + // Helper method to flag reactions that can be processed deterministically (continuous change) + // without exceeding the user-supplied tolerance + + std::set flag_det_rxns(const Model &model, + const std::map det_species, + std::map det_rxns, + std::map> dependent_species){ + int number_rxns = model.number_reactions; + for (int rxn = 0; rxn < number_rxns; ++rxn){ + // start with the assumption that reaction is determinstic + det_rxns[rxn] = true; + // iterate through the dependent species of this reaction + for (int s = 0; s < dependent_species[rxn].size(); ++s){ + // if any of the dependencies are set by the user as discrete OR + // have been set as dynamic and has not been flagged as deterministic, + // allow it to be modelled discretely + if (model.species[s].user_mode == DISCRETE){ + det_rxns[rxn] = false; + break; + } + if (model.species[s].user_mode == DYNAMIC && det_species[s] == false) { + det_rxns[rxn] = false; + break; + } + } + } + //create a set of all deterministic reactions + std::set new_deterministic_reactions; + for (int rxn = 0; rxn < det_rxns.size(); ++rxn) { + if (det_rxns[rxn] == true){ + new_deterministic_reactions.insert(rxn); + } + } + return new_deterministic_reactions; + } + void partition_species(const Model &model, const std::vector &propensity_values, std::vector curr_state, double tau_step, double current_time, std::map &det_species) + { + // coefficient of variance- key:species id, value: cv + std::map cv; + // means + std::map means; + // standard deviation + std::map sd; + //init means + for (int i = 0; i < model.number_species; ++i) + { + if (model.species[i].user_mode == DYNAMIC) + { + if (model.species[i].partition_mode == CONTINUOUS) + { + means.insert({i, curr_state[i].continuous}); + } + else + { + means.insert({i, curr_state[i].discrete}); + } + } + } + //init sd's + for (int i = 0; i < model.number_species; ++i) + { + if (model.species[i].user_mode == DYNAMIC) + { + sd.insert({i, 0}); + } + } + // calculate means and standard deviations for dynamic-mode species involved in reactions + for (int r = 0; r < model.number_reactions; ++r) + { + for (int s = 0; s < model.number_species; ++s) + { + // access list of species by accessing the correct element of the state-change vector (of the reaction) + if (model.species[model.reactions[r].species_change[s]].user_mode == DYNAMIC) + { + // if less than 0, that means this is a reactant + if (model.reactions[r].species_change[s] < 0) + { + means[s] -= (tau_step * propensity_values[r] * model.reactions[r].species_change[s]); + sd[s] += std::pow((tau_step * propensity_values[r] * model.reactions[r].species_change[s]), 2); + } + // if greater than 0, that means this is a product + if (model.reactions[r].species_change[s] > 0) + { + means[s] += (tau_step * propensity_values[r] * model.reactions[r].species_change[s]); + sd[s] += std::pow((tau_step * propensity_values[r] * model.reactions[r].species_change[s]), 2); + } + } + } + } + // calculate coefficient of variation using means and sd + for (int s = 0; s < model.number_species; ++s) + { + if (means.count(s) > 0) + { + Species sref = model.species[s]; + if (sref.switch_min == 0) + { // (default value means switch min not set, use switch tol) + if (means[s] > 0) + { + cv[s] = sd[s] / means[s]; + } + else + { + cv[s] = 1; + } + if (cv[s] < sref.switch_tol) + { + sref.partition_mode == CONTINUOUS; + } + else + { + sref.partition_mode == DISCRETE; + } + } + else + { + if (means[s] > sref.switch_min) + { + sref.partition_mode == CONTINUOUS; + } + else + { + sref.partition_mode == DISCRETE; + } + } + } + } + return //TODO; + } +} diff --git a/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/hybridutils.h b/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/hybridutils.h new file mode 100644 index 000000000..e69de29bb From 162771314933b2de8edd6bd359edb4b04c9f78cc Mon Sep 17 00:00:00 2001 From: Josh C Date: Tue, 1 Jun 2021 18:26:10 -0400 Subject: [PATCH 45/88] Fix segmentation faults in integrator RHS - Allocate proper amount of memory in CVODE init - Change stride in RHS access to `dydt` output vector --- .../tau_hybrid_cpp_solver/HybridModel.h | 2 ++ .../TauHybridCSolver.cpp | 24 +++++++++---------- .../tau_hybrid_cpp_solver/TauHybridCSolver.h | 2 +- .../TauHybridTemplate.cpp | 2 +- .../cpp/c_base/tau_hybrid_cpp_solver/makefile | 14 +++++------ gillespy2/solvers/cpp/ssa_c_solver.py | 5 ++-- 6 files changed, 26 insertions(+), 23 deletions(-) diff --git a/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/HybridModel.h b/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/HybridModel.h index fd1cea4f1..e149eb8d6 100644 --- a/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/HybridModel.h +++ b/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/HybridModel.h @@ -1,3 +1,5 @@ +#pragma once + #include #include "model.h" diff --git a/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/TauHybridCSolver.cpp b/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/TauHybridCSolver.cpp index e1ea463fc..2e7f922ed 100644 --- a/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/TauHybridCSolver.cpp +++ b/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/TauHybridCSolver.cpp @@ -9,7 +9,7 @@ #include "sundials_math.h" // contains the macros ABS, SUNSQR, EXP #include "TauHybridCSolver.h" #include "HybridModel.h" -#include "statistics.h" +// #include "statistics.h" #include "tau.h" using namespace Gillespy; @@ -101,7 +101,7 @@ namespace Gillespy::TauHybrid { // The first half of the integration vector is used for integrating species concentrations. // [ --- concentrations --- | ... - N_Vector y0 = N_VNew_Serial(num_species); + N_Vector y0 = N_VNew_Serial(rxn_offset_boundary); for (int spec_i = 0; spec_i < num_species; ++spec_i) { NV_Ith_S(y0, spec_i) = species[spec_i].initial_population; } @@ -207,13 +207,13 @@ namespace Gillespy::TauHybrid { // Output the results for this time step. simulation->current_time = next_time; - while (save_time <= next_time) { - // Write each species, one at a time (from ODE solution) - for (int spec_i = 0; spec_i < num_species; ++spec_i) { - simulation->trajectoriesODE[traj][save_time][spec_i] = NV_Ith_S(y0, spec_i); - } - save_time += increment; - } + // while (save_time <= next_time) { + // // Write each species, one at a time (from ODE solution) + // for (int spec_i = 0; spec_i < num_species; ++spec_i) { + // simulation->trajectoriesODE[traj][save_time][spec_i] = NV_Ith_S(y0, spec_i); + // } + // save_time += increment; + // } } @@ -263,7 +263,7 @@ static int f(realtype t, N_Vector y, N_Vector ydot, void *user_data) unsigned int rxn_i; unsigned int rxn_offset_i; int species_change; - for (rxn_i = 0; rxn_i < sim->model->number_reactions; ++rxn_i) { + for (rxn_i = 0; rxn_i < num_reactions; ++rxn_i) { // Index to the reaction's offset state in the integrator. rxn_offset_i = rxn_i + num_species; @@ -272,9 +272,9 @@ static int f(realtype t, N_Vector y, N_Vector ydot, void *user_data) propensity = sim->propensity_function->ODEEvaluate(rxn_i, concentrations); // Integrate this reaction's rxn_offset forward using the propensity. - rxn_offsets[rxn_offset_i] = Y[rxn_offset_i] + propensity; + dydt[rxn_offset_i] = rxn_offsets[rxn_i] + propensity; - for (spec_i = 0; spec_i < sim->model->number_species; ++spec_i) { + for (spec_i = 0; spec_i < num_species; ++spec_i) { // Use the evaluated propensity to update the concentration levels and reaction state. // Propensity is treated as positive if it's a product, negative if it's a reactant. species_change = sim->model->reactions[rxn_i].species_change[spec_i]; diff --git a/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/TauHybridCSolver.h b/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/TauHybridCSolver.h index 7af3b5a61..8d8624964 100644 --- a/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/TauHybridCSolver.h +++ b/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/TauHybridCSolver.h @@ -2,7 +2,7 @@ #define TAUHYBRIDCSOLVER_H #include "model.h" -namespace Gillespy { +namespace Gillespy::TauHybrid { void TauHybridCSolver(Gillespy::Simulation* simulation, const double tau_tol); } diff --git a/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/TauHybridTemplate.cpp b/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/TauHybridTemplate.cpp index e76dc935e..0b1652621 100644 --- a/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/TauHybridTemplate.cpp +++ b/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/TauHybridTemplate.cpp @@ -91,7 +91,7 @@ __DEFINE_REACTIONS_ simulation.propensity_function = propFun; simulationINIT(&model, simulation); // Perform ODE // - TauHybridCSolver(&simulation, 0.05); + TauHybrid::TauHybridCSolver(&simulation, 0.05); simulation.output_results_buffer(std :: cout); delete propFun; return 0; diff --git a/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/makefile b/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/makefile index 4ce7dcd88..62303d18c 100644 --- a/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/makefile +++ b/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/makefile @@ -1,8 +1,8 @@ # SUNDIALS_DIR = ../Sundials # CBASE_DIR = ../ -CC=g++ -CFLAGS = -c -std=c++14 -Wall -O3 +CXX=g++ +CXXFLAGS = -c -std=c++14 -Wall -O3 LINKFLAGS = -L. -std=c++14 -Wall -O3 SRCDIR = $(SUNDIALS_DIR)/src INCDIR = $(SUNDIALS_DIR)/include @@ -21,19 +21,19 @@ sundials_nvector.o nvector_serial.o cvode.o cvode_spils.o sundials_math.o sunlin all: model.o TauHybridCSolver.o TauHybridSimulation.o TauHybridSimulation model.o: - $(CC) -c -o model.o $(CBASE_DIR)/model.cpp $(CFLAGS) -I$(CBASE_DIR) -I$(INCDIR) + $(CXX) -c -o model.o $(CBASE_DIR)/model.cpp $(CXXFLAGS) -I$(CBASE_DIR) -I$(INCDIR) %.o: $(SRCDIR)/%.c - $(CC) -c -o $@ $< $(CFLAGS) -I$(INCDIR) + $(CXX) -c -o $@ $< $(CXXFLAGS) -I$(INCDIR) TauHybridCSolver.o: - $(CC) -c -o TauHybridCSolver.o $(GILLESPY_CPP_TAU_HYBRID_DIR)/TauHybridCSolver.cpp $(CFLAGS) -I$(CBASE_DIR) -I$(SRCDIR) -I$(INCDIR) -I$(CBASE_DIR)/Tau + $(CXX) -c -o TauHybridCSolver.o $(GILLESPY_CPP_TAU_HYBRID_DIR)/TauHybridCSolver.cpp $(CXXFLAGS) -I$(CBASE_DIR) -I$(SRCDIR) -I$(INCDIR) -I$(CBASE_DIR)/Tau TauHybridSimulation.o: - $(CC) -c -o TauHybridSimulation.o TauHybridSimulation.cpp $(CFLAGS) -I$(CBASE_DIR) -I$(GILLESPY_CPP_TAU_HYBRID_DIR) + $(CXX) -c -o TauHybridSimulation.o TauHybridSimulation.cpp $(CXXFLAGS) -I$(CBASE_DIR) -I$(GILLESPY_CPP_TAU_HYBRID_DIR) TauHybridSimulation: TauHybridSimulation.o TauHybridCSolver.o $(SUNOBJ) model.o - $(CC) -o TauHybridSimulation TauHybridCSolver.o TauHybridSimulation.o $(SUNOBJ) model.o $(LINKFLAGS) + $(CXX) -o TauHybridSimulation TauHybridCSolver.o TauHybridSimulation.o $(SUNOBJ) model.o $(LINKFLAGS) cleanSimulationODE: rm -f TauHybridSimulation diff --git a/gillespy2/solvers/cpp/ssa_c_solver.py b/gillespy2/solvers/cpp/ssa_c_solver.py index 1cc94dc86..247148803 100644 --- a/gillespy2/solvers/cpp/ssa_c_solver.py +++ b/gillespy2/solvers/cpp/ssa_c_solver.py @@ -214,6 +214,7 @@ def read_next(): platform_args = { "start_new_session": True } thread_events = { "timeout": False } + return_code = 0 with subprocess.Popen(args, stdout=subprocess.PIPE, **platform_args) as simulation: # Put a timer on in the background, if a timeout was specified. def timeout_kill(): @@ -237,12 +238,12 @@ def timeout_kill(): finally: # Check if the simulation had been paused # (Necessary because we can't set pause to True from thread handler) - if reader.timed_out: + if thread_events["timeout"]: pause = True return_code = 33 # Decode from byte, split by comma into array - stdout = stdout.split(",") + stdout = "".join(buffer).split(",") if return_code in [0, 33]: trajectory_base, timeStopped = cutils.parse_binary_output(number_of_trajectories, number_timesteps, len(model.listOfSpecies), stdout, pause=pause) From 0337292af4da04e4f1462d8a8b1945da9730cf17 Mon Sep 17 00:00:00 2001 From: Josh C Date: Wed, 2 Jun 2021 10:57:28 -0400 Subject: [PATCH 46/88] Hybrid simulation/model inheritance changes - Add `HybridSimulation` and changes to `HybridModel` - Add enum for differentiating continuous/discrete trajectories - Temporary hybrid-specific output function - Hybrid-specific trajectories array using `hybrid_state` union --- gillespy2/solvers/cpp/c_base/model.cpp | 30 ++++----- gillespy2/solvers/cpp/c_base/model.h | 5 +- .../tau_hybrid_cpp_solver/HybridModel.h | 28 ++++++++- .../TauHybridCSolver.cpp | 63 ++++++++++++++++--- .../tau_hybrid_cpp_solver/TauHybridCSolver.h | 4 +- .../TauHybridTemplate.cpp | 6 +- 6 files changed, 97 insertions(+), 39 deletions(-) diff --git a/gillespy2/solvers/cpp/c_base/model.cpp b/gillespy2/solvers/cpp/c_base/model.cpp index 2519b79d3..9a4fe9340 100644 --- a/gillespy2/solvers/cpp/c_base/model.cpp +++ b/gillespy2/solvers/cpp/c_base/model.cpp @@ -41,13 +41,19 @@ namespace Gillespy{ } } - void simulationSSAINIT(Model* model, Simulation &simulation){ + void init_timeline(Simulation &simulation) + { simulation.timeline = new double[simulation.number_timesteps]; double timestep_size = simulation.end_time/(simulation.number_timesteps-1); for(unsigned int i = 0; i < simulation.number_timesteps; i++){ simulation.timeline[i] = timestep_size * i; } - unsigned int trajectory_size = simulation.number_timesteps * (model -> number_species); + } + + void simulationSSAINIT(Model* model, Simulation &simulation){ + init_timeline(simulation); + + unsigned int trajectory_size = simulation.number_timesteps * (model -> number_species); simulation.trajectories_1D = new unsigned int[simulation.number_trajectories * trajectory_size]; simulation.trajectories = new unsigned int**[simulation.number_trajectories]; for(unsigned int i = 0; i < simulation.number_trajectories; i++){ @@ -60,11 +66,8 @@ namespace Gillespy{ // initialize timeline, void simulationODEINIT(Model* model, Simulation &simulation){ - simulation.timeline = new double[simulation.number_timesteps]; - double timestep_size = simulation.end_time/(simulation.number_timesteps-1); - for(unsigned int i = 0; i < simulation.number_timesteps; i++){ - simulation.timeline[i] = timestep_size * i; - } + init_timeline(simulation); + unsigned int trajectory_size = simulation.number_timesteps * (model -> number_species); simulation.trajectories_1DODE = new double[simulation.number_trajectories * trajectory_size]; simulation.trajectoriesODE = new double**[simulation.number_trajectories]; @@ -90,14 +93,14 @@ namespace Gillespy{ Simulation :: ~Simulation(){ int type = this->type; delete timeline; - if (type == ODE || type == HYBRID){ + if (type == ODE){ delete trajectories_1DODE; for(unsigned int i = 0; i < number_trajectories; i++){ delete trajectoriesODE[i]; } delete trajectoriesODE; } - else if (type == SSA || type == TAU || type == HYBRID){ + else if (type == SSA || type == TAU){ delete trajectories_1D; for(unsigned int i = 0; i < number_trajectories; i++){ delete trajectories[i]; @@ -132,15 +135,6 @@ namespace Gillespy{ else if (type == SSA || type == TAU){ os< namespace Gillespy{ - #define CONTINUOUS 0 - #define DISCRETE 1 - #define DYNAMIC 2 //Represents info for a chemical reactant/product struct Species{ unsigned int id; //useful for index id in arrays @@ -82,13 +79,13 @@ namespace Gillespy{ // this is essentially just an array that tracks whether or not a given species was computed continuously or discretely for a given timestep and trajectory (iteration of simulation) // CONTINUOUS 0 // DISCRETE 1 - int*** trajectoriesHYBRID; IPropensityFunction *propensity_function; friend std :: ostream& operator<<(std :: ostream& os, const Simulation& simulation); void output_results_buffer(std :: ostream& os); }; //Trajectory initializers for ODE and SSA solvers + void init_timeline(Simulation &simulation); // void simulationODEINIT(Model* model, Simulation &simulation); // void simulationSSAINIT(Model* model, Simulation &simulation); void simulationINIT(Model* model, Simulation &simulation); diff --git a/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/HybridModel.h b/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/HybridModel.h index e149eb8d6..988d289bb 100644 --- a/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/HybridModel.h +++ b/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/HybridModel.h @@ -3,28 +3,37 @@ #include #include "model.h" -namespace Gillespy { +namespace Gillespy::TauHybrid { #define GPY_HYBRID_ABSTOL 1e-5 #define GPY_HYBRID_RELTOL 1e-5 struct HybridSpecies : Species { + enum SpeciesState { + CONTINUOUS = 0, + DISCRETE, + DYNAMIC + }; + // allows the user to specify if a species' population should definitely be modeled continuously or // discretely // CONTINUOUS or DISCRETE // otherwise, mode will be determined by the program (DYNAMIC) // if no choice is made, DYNAMIC will be assumed - int user_mode; + int user_mode : 2; + // during simulation execution, a species will fall into either of the two categories, CONTINUOUS or DISCRETE // this is pre-determined only if the user_mode specifies CONTINUOUS or DISCRETE. // otherwise, if DYNAMIC is specified, partition_mode will be continually calculated throughout the simulation // according to standard deviation and coefficient of variance. - int partition_mode; + int partition_mode : 1; + // Tolerance level for considering a dynamic species deterministically, value is compared // to an estimated sd/mean population of a species after a given time step. // This value will be used if a switch_min is not provided. The default value is 0.03 double switch_tol = 0.03; + //Minimum population value at which species will be represented as continuous. // If a value is given, switch_min will be used instead of switch_tol. unsigned int switch_min = 0; @@ -35,4 +44,17 @@ namespace Gillespy { unsigned int discrete; double continuous; }; + + struct HybridSimulation : Simulation + { + hybrid_state *trajectories_hybrid1D; + hybrid_state ***trajectories_hybrid; + + void output_hybrid_results(std::ostream &os); + + ~HybridSimulation(); + }; + + void simulation_hybrid_init(HybridSimulation &simulation); + } diff --git a/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/TauHybridCSolver.cpp b/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/TauHybridCSolver.cpp index 2e7f922ed..59efac921 100644 --- a/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/TauHybridCSolver.cpp +++ b/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/TauHybridCSolver.cpp @@ -41,7 +41,51 @@ namespace Gillespy::TauHybrid { interrupted = true; } - void TauHybridCSolver(Gillespy::Simulation *simulation, const double tau_tol) + void simulation_hybrid_init(HybridSimulation &simulation) + { + Model *model = simulation.model; + init_timeline(simulation); + simulation.type = HYBRID; + + unsigned int trajectory_size = simulation.number_timesteps * (model -> number_species); + /* 1-dimensional arrays might be unnecessary; look into mapping 3D array directly */ + simulation.trajectories_hybrid1D = new hybrid_state[simulation.number_trajectories * trajectory_size]; + simulation.trajectories_hybrid = new hybrid_state**[simulation.number_trajectories]; + + for(unsigned int i = 0; i < simulation.number_trajectories; i++){ + simulation.trajectories_hybrid[i] = new hybrid_state*[simulation.number_timesteps]; + for(unsigned int j = 0; j < simulation.number_timesteps; j++){ + simulation.trajectories_hybrid[i][j] = &(simulation.trajectories_hybrid1D[i * trajectory_size + j * (model -> number_species)]); + } + } + } + + void HybridSimulation::output_hybrid_results(std::ostream &os) + { + for (int i = 0 ; i < number_trajectories; i++){ + for (int j = 0; j < number_timesteps; j++){ + os << timeline[j] << ','; + + for (int k = 0; k < model->number_species; k++) { + os << trajectories_hybrid[i][j][k].continuous << ','; + } + } + + os<<(int)current_time; + } + } + + HybridSimulation::~HybridSimulation() + { + if (type == HYBRID) { + for(unsigned int i = 0; i < number_trajectories; i++){ + delete trajectories_hybrid[i]; + } + delete trajectories_hybrid; + } + } + + void TauHybridCSolver(HybridSimulation *simulation, const double tau_tol) { //timeouts not supported right now?? signal(SIGINT, signalHandler); @@ -70,7 +114,7 @@ namespace Gillespy::TauHybrid { //copy initial state for each trajectory for(int s = 0; s < num_species; s++){ - simulation->trajectoriesODE[0][0][s] = species[s].initial_population; + simulation->trajectories_hybrid[0][0][s].continuous = species[s].initial_population; current_state[s] = species[s].initial_population; } //Simulate for each trajectory @@ -138,6 +182,7 @@ namespace Gillespy::TauHybrid { // Temporary array to store changes to dependent species. // Should be 0-initialized each time it's used. int *population_changes = new int[num_species]; + simulation->current_time = 0; while (simulation->current_time < simulation->end_time) { // Determine what the next time point is. // This will become current_time on the next iteration. @@ -207,13 +252,13 @@ namespace Gillespy::TauHybrid { // Output the results for this time step. simulation->current_time = next_time; - // while (save_time <= next_time) { - // // Write each species, one at a time (from ODE solution) - // for (int spec_i = 0; spec_i < num_species; ++spec_i) { - // simulation->trajectoriesODE[traj][save_time][spec_i] = NV_Ith_S(y0, spec_i); - // } - // save_time += increment; - // } + while (save_time <= next_time) { + // Write each species, one at a time (from ODE solution) + for (int spec_i = 0; spec_i < num_species; ++spec_i) { + simulation->trajectories_hybrid[traj][save_time][spec_i].continuous = NV_Ith_S(y0, spec_i); + } + save_time += increment; + } } diff --git a/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/TauHybridCSolver.h b/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/TauHybridCSolver.h index 8d8624964..3c340402d 100644 --- a/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/TauHybridCSolver.h +++ b/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/TauHybridCSolver.h @@ -1,9 +1,9 @@ #ifndef TAUHYBRIDCSOLVER_H #define TAUHYBRIDCSOLVER_H -#include "model.h" +#include "HybridModel.h" namespace Gillespy::TauHybrid { - void TauHybridCSolver(Gillespy::Simulation* simulation, const double tau_tol); + void TauHybridCSolver(HybridSimulation* simulation, const double tau_tol); } #endif \ No newline at end of file diff --git a/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/TauHybridTemplate.cpp b/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/TauHybridTemplate.cpp index 0b1652621..5557d0e9b 100644 --- a/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/TauHybridTemplate.cpp +++ b/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/TauHybridTemplate.cpp @@ -79,7 +79,7 @@ __DEFINE_REACTIONS_ } IPropensityFunction *propFun = new PropensityFunction(); //Simulation INIT - Simulation simulation; + TauHybrid::HybridSimulation simulation; Model* modelptr; modelptr = &model; simulation.type = HYBRID; @@ -89,10 +89,10 @@ __DEFINE_REACTIONS_ simulation.number_timesteps = number_timesteps; simulation.number_trajectories = number_trajectories; simulation.propensity_function = propFun; - simulationINIT(&model, simulation); + TauHybrid::simulation_hybrid_init(simulation); // Perform ODE // TauHybrid::TauHybridCSolver(&simulation, 0.05); - simulation.output_results_buffer(std :: cout); + simulation.output_hybrid_results(std :: cout); delete propFun; return 0; } \ No newline at end of file From 2432d76a88467a1810fa01e20c7c4c7eea0ae92b Mon Sep 17 00:00:00 2001 From: Josh C Date: Wed, 2 Jun 2021 14:10:43 -0400 Subject: [PATCH 47/88] Hybrid propensity function on hybrid simulation class - New instance method, `hybrid_propensity` - Computes stochastic propensities (same as TauEvaluate) but accepts doubles --- .../tau_hybrid_cpp_solver/HybridModel.h | 2 ++ .../TauHybridCSolver.cpp | 36 ++++++++++++------- .../TauHybridTemplate.cpp | 12 +++++++ 3 files changed, 37 insertions(+), 13 deletions(-) diff --git a/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/HybridModel.h b/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/HybridModel.h index 988d289bb..8915003b6 100644 --- a/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/HybridModel.h +++ b/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/HybridModel.h @@ -39,6 +39,7 @@ namespace Gillespy::TauHybrid { unsigned int switch_min = 0; }; + union hybrid_state { unsigned int discrete; @@ -51,6 +52,7 @@ namespace Gillespy::TauHybrid { hybrid_state ***trajectories_hybrid; void output_hybrid_results(std::ostream &os); + double hybrid_propensity(int reaction_id, std::vector ¤t_state); ~HybridSimulation(); }; diff --git a/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/TauHybridCSolver.cpp b/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/TauHybridCSolver.cpp index 59efac921..86974b4ab 100644 --- a/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/TauHybridCSolver.cpp +++ b/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/TauHybridCSolver.cpp @@ -18,7 +18,7 @@ using namespace Gillespy; static int f(realtype t, N_Vector y, N_Vector y_dot, void *user_data); // forward declare function to be used to solve RHS of ODE struct UserData { - Gillespy::Simulation *my_sim; + Gillespy::TauHybrid::HybridSimulation *my_sim; }; struct IntegratorOptions{ // CVODE constants returned if bad output, or success output. @@ -88,7 +88,7 @@ namespace Gillespy::TauHybrid { void TauHybridCSolver(HybridSimulation *simulation, const double tau_tol) { //timeouts not supported right now?? - signal(SIGINT, signalHandler); + // signal(SIGINT, signalHandler); if (simulation) { int num_species = (simulation->model)->number_species; int num_reactions = (simulation->model)->number_reactions; @@ -287,38 +287,42 @@ static int f(realtype t, N_Vector y, N_Vector ydot, void *user_data) // Extract simulation data UserData *data = static_cast(user_data); - Simulation *sim = data->my_sim; + Gillespy::TauHybrid::HybridSimulation *sim = data->my_sim; unsigned int num_species = sim->model->number_species; unsigned int num_reactions = sim->model->number_reactions; realtype *rxn_offsets = &Y[num_species]; + realtype *dydt_offsets = &dydt[num_species]; int rxn_offset_boundary = num_species + num_reactions; - std::vector concentrations(sim->model->number_species); + std::vector concentrations(num_species); + std::vector populations(num_species); // Populate the current ODE state into the concentrations vector. // dy/dt results are initialized to zero, and become the change in propensity. unsigned int spec_i; for (spec_i = 0; spec_i < sim->model->number_species; ++spec_i) { - concentrations[spec_i] = Y[spec_i]; + populations[spec_i] = concentrations[spec_i] = Y[spec_i]; dydt[spec_i] = 0; } + // Populate the current stochastic state into the root offset vector. + // dy/dt results are initialized to zero, and become the change in offset. + unsigned int rxn_i; + for (rxn_i = 0; rxn_i < num_reactions; ++rxn_i) { + dydt_offsets[rxn_i] = 0; + } + // Each species has a "spot" in the y and f(y,t) vector. // For each species, place the result of f(y,t) into dydt vector. - unsigned int rxn_i; - unsigned int rxn_offset_i; int species_change; - for (rxn_i = 0; rxn_i < num_reactions; ++rxn_i) { - // Index to the reaction's offset state in the integrator. - rxn_offset_i = rxn_i + num_species; + // Process deterministic propensity state + // These updates get written directly to the integrator's concentration state + for (rxn_i = 0; rxn_i < num_reactions; ++rxn_i) { // NOTE: we may need to evaluate ODE and Tau propensities separately. // At the moment, it's unsure whether or not that's required. propensity = sim->propensity_function->ODEEvaluate(rxn_i, concentrations); - // Integrate this reaction's rxn_offset forward using the propensity. - dydt[rxn_offset_i] = rxn_offsets[rxn_i] + propensity; - for (spec_i = 0; spec_i < num_species; ++spec_i) { // Use the evaluated propensity to update the concentration levels and reaction state. // Propensity is treated as positive if it's a product, negative if it's a reactant. @@ -331,6 +335,12 @@ static int f(realtype t, N_Vector y, N_Vector ydot, void *user_data) // This is a branchless alternative to using an if-statement. dydt[spec_i] += propensity * (-1 + 2 * (species_change > 0)); } + + // Process stochastic reaction state by updating the root offset for each reaction. + propensity = sim->hybrid_propensity(rxn_i, concentrations); + + // Integrate this reaction's rxn_offset forward using the propensity. + dydt_offsets[rxn_i] += rxn_offsets[rxn_i] + propensity; } return 0; diff --git a/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/TauHybridTemplate.cpp b/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/TauHybridTemplate.cpp index 5557d0e9b..f5daa182f 100644 --- a/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/TauHybridTemplate.cpp +++ b/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/TauHybridTemplate.cpp @@ -34,6 +34,18 @@ __DEFINE_PROPENSITY__ double evaluate(unsigned int reaction_number, unsigned int* S){return 1.0;} }; +double Gillespy::TauHybrid::HybridSimulation::hybrid_propensity( + int reaction_id, + std::vector &S) +{ + switch (reaction_id) { +__DEFINE_PROPENSITY__ + + default: + return -1.0; + } +} + int main(int argc, char* argv[]){ std :: vector species_names(s_names, s_names + sizeof(s_names)/sizeof(std :: string)); std :: vector species_populations(populations, populations + sizeof(populations)/sizeof(populations[0])); From 52b10428c5c22e3a5a2a80b73349683435b8f071 Mon Sep 17 00:00:00 2001 From: Josh C Date: Mon, 7 Jun 2021 10:25:44 -0400 Subject: [PATCH 48/88] Separate base classes from runtime mutable state - Modifies custom `Hybrid*` structs to not be derived from base - Instead accepts a pointer to the relevant Species/Reaction/Model/etc. - Update integrator to use array of custom structs --- .../tau_hybrid_cpp_solver/HybridModel.h | 118 ++++---- .../TauHybridCSolver.cpp | 268 ++++++++++-------- 2 files changed, 218 insertions(+), 168 deletions(-) diff --git a/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/HybridModel.h b/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/HybridModel.h index 8915003b6..985e5269a 100644 --- a/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/HybridModel.h +++ b/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/HybridModel.h @@ -3,60 +3,72 @@ #include #include "model.h" +#define GPY_HYBRID_ABSTOL 1e-5 +#define GPY_HYBRID_RELTOL 1e-5 + namespace Gillespy::TauHybrid { - #define GPY_HYBRID_ABSTOL 1e-5 - #define GPY_HYBRID_RELTOL 1e-5 - - struct HybridSpecies : Species - { - enum SpeciesState { - CONTINUOUS = 0, - DISCRETE, - DYNAMIC - }; - - // allows the user to specify if a species' population should definitely be modeled continuously or - // discretely - // CONTINUOUS or DISCRETE - // otherwise, mode will be determined by the program (DYNAMIC) - // if no choice is made, DYNAMIC will be assumed - int user_mode : 2; - - // during simulation execution, a species will fall into either of the two categories, CONTINUOUS or DISCRETE - // this is pre-determined only if the user_mode specifies CONTINUOUS or DISCRETE. - // otherwise, if DYNAMIC is specified, partition_mode will be continually calculated throughout the simulation - // according to standard deviation and coefficient of variance. - int partition_mode : 1; - - // Tolerance level for considering a dynamic species deterministically, value is compared - // to an estimated sd/mean population of a species after a given time step. - // This value will be used if a switch_min is not provided. The default value is 0.03 - double switch_tol = 0.03; - - //Minimum population value at which species will be represented as continuous. - // If a value is given, switch_min will be used instead of switch_tol. - unsigned int switch_min = 0; - }; - - - union hybrid_state - { - unsigned int discrete; - double continuous; - }; - - struct HybridSimulation : Simulation - { - hybrid_state *trajectories_hybrid1D; - hybrid_state ***trajectories_hybrid; - - void output_hybrid_results(std::ostream &os); - double hybrid_propensity(int reaction_id, std::vector ¤t_state); - - ~HybridSimulation(); - }; - - void simulation_hybrid_init(HybridSimulation &simulation); + enum SimulationState + { + CONTINUOUS = 0, + DISCRETE, + DYNAMIC + }; + + struct HybridSpecies + { + Species *base_species; + + // allows the user to specify if a species' population should definitely be modeled continuously or + // discretely + // CONTINUOUS or DISCRETE + // otherwise, mode will be determined by the program (DYNAMIC) + // if no choice is made, DYNAMIC will be assumed + SimulationState user_mode : 2; + + // during simulation execution, a species will fall into either of the two categories, CONTINUOUS or DISCRETE + // this is pre-determined only if the user_mode specifies CONTINUOUS or DISCRETE. + // otherwise, if DYNAMIC is specified, partition_mode will be continually calculated throughout the simulation + // according to standard deviation and coefficient of variance. + SimulationState partition_mode : 1; + + // Tolerance level for considering a dynamic species deterministically, value is compared + // to an estimated sd/mean population of a species after a given time step. + // This value will be used if a switch_min is not provided. The default value is 0.03 + double switch_tol = 0.03; + + //Minimum population value at which species will be represented as continuous. + // If a value is given, switch_min will be used instead of switch_tol. + unsigned int switch_min = 0; + + HybridSpecies(); + }; + + struct HybridReaction + { + Reaction *base_reaction; + SimulationState mode : 1; + + HybridReaction(); + }; + + union hybrid_state + { + unsigned int discrete; + double continuous; + }; + + struct HybridSimulation : Simulation + { + hybrid_state *trajectories_hybrid1D; + hybrid_state ***trajectories_hybrid; + + void output_hybrid_results(std::ostream &os); + double hybrid_propensity(int reaction_id, std::vector ¤t_state); + + ~HybridSimulation(); + }; + + void simulation_hybrid_init(HybridSimulation &simulation); } diff --git a/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/TauHybridCSolver.cpp b/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/TauHybridCSolver.cpp index 86974b4ab..0d4d8cad9 100644 --- a/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/TauHybridCSolver.cpp +++ b/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/TauHybridCSolver.cpp @@ -18,20 +18,22 @@ using namespace Gillespy; static int f(realtype t, N_Vector y, N_Vector y_dot, void *user_data); // forward declare function to be used to solve RHS of ODE struct UserData { - Gillespy::TauHybrid::HybridSimulation *my_sim; + Gillespy::TauHybrid::HybridSimulation *my_sim; + Gillespy::TauHybrid::HybridSpecies *species_state; + Gillespy::TauHybrid::HybridReaction *reaciton_state; }; struct IntegratorOptions{ - // CVODE constants returned if bad output, or success output. - // Constants: CV_SUCCESS, - // CV_MEM_NULL: CVODE memory block not initialized through call to CVodeCreate - // CV_NO_MALLOC: The allocation function CVodeInit not called - // CV_ILL_Input: An input tolerance was negative - int flag; - // absolute tolerace of a system - realtype abstol; - // relative tolerance of system - realtype reltol; - // double max_step; + // CVODE constants returned if bad output, or success output. + // Constants: CV_SUCCESS, + // CV_MEM_NULL: CVODE memory block not initialized through call to CVodeCreate + // CV_NO_MALLOC: The allocation function CVodeInit not called + // CV_ILL_Input: An input tolerance was negative + int flag; + // absolute tolerace of a system + realtype abstol; + // relative tolerance of system + realtype reltol; + // double max_step; }; namespace Gillespy::TauHybrid { bool interrupted = false; @@ -41,63 +43,90 @@ namespace Gillespy::TauHybrid { interrupted = true; } - void simulation_hybrid_init(HybridSimulation &simulation) - { - Model *model = simulation.model; - init_timeline(simulation); - simulation.type = HYBRID; - - unsigned int trajectory_size = simulation.number_timesteps * (model -> number_species); - /* 1-dimensional arrays might be unnecessary; look into mapping 3D array directly */ - simulation.trajectories_hybrid1D = new hybrid_state[simulation.number_trajectories * trajectory_size]; - simulation.trajectories_hybrid = new hybrid_state**[simulation.number_trajectories]; - - for(unsigned int i = 0; i < simulation.number_trajectories; i++){ - simulation.trajectories_hybrid[i] = new hybrid_state*[simulation.number_timesteps]; - for(unsigned int j = 0; j < simulation.number_timesteps; j++){ - simulation.trajectories_hybrid[i][j] = &(simulation.trajectories_hybrid1D[i * trajectory_size + j * (model -> number_species)]); - } - } - } - - void HybridSimulation::output_hybrid_results(std::ostream &os) - { - for (int i = 0 ; i < number_trajectories; i++){ - for (int j = 0; j < number_timesteps; j++){ - os << timeline[j] << ','; - - for (int k = 0; k < model->number_species; k++) { - os << trajectories_hybrid[i][j][k].continuous << ','; - } - } - - os<<(int)current_time; - } - } - - HybridSimulation::~HybridSimulation() - { - if (type == HYBRID) { - for(unsigned int i = 0; i < number_trajectories; i++){ - delete trajectories_hybrid[i]; - } - delete trajectories_hybrid; - } - } + void simulation_hybrid_init(HybridSimulation &simulation) + { + Model *model = simulation.model; + init_timeline(simulation); + simulation.type = HYBRID; + + unsigned int trajectory_size = simulation.number_timesteps * (model -> number_species); + /* 1-dimensional arrays might be unnecessary; look into mapping 3D array directly */ + simulation.trajectories_hybrid1D = new hybrid_state[simulation.number_trajectories * trajectory_size]; + simulation.trajectories_hybrid = new hybrid_state**[simulation.number_trajectories]; + + for(unsigned int i = 0; i < simulation.number_trajectories; i++){ + simulation.trajectories_hybrid[i] = new hybrid_state*[simulation.number_timesteps]; + for(unsigned int j = 0; j < simulation.number_timesteps; j++){ + simulation.trajectories_hybrid[i][j] = &(simulation.trajectories_hybrid1D[i * trajectory_size + j * (model -> number_species)]); + } + } + } + + void HybridSimulation::output_hybrid_results(std::ostream &os) + { + for (int i = 0 ; i < number_trajectories; i++){ + for (int j = 0; j < number_timesteps; j++){ + os << timeline[j] << ','; + + for (int k = 0; k < model->number_species; k++) { + os << trajectories_hybrid[i][j][k].continuous << ','; + } + } + + os<<(int)current_time; + } + } + + HybridReaction::HybridReaction() + : mode(SimulationState::CONTINUOUS), + base_reaction(nullptr) + { + // Empty constructor body + } + + HybridSpecies::HybridSpecies() + : user_mode(SimulationState::CONTINUOUS), + partition_mode(SimulationState::CONTINUOUS), + switch_tol(0.03), + switch_min(0) + { + // Empty constructor body + } + + HybridSimulation::~HybridSimulation() + { + if (type == HYBRID) { + for(unsigned int i = 0; i < number_trajectories; i++){ + delete trajectories_hybrid[i]; + } + delete trajectories_hybrid; + } + } void TauHybridCSolver(HybridSimulation *simulation, const double tau_tol) { //timeouts not supported right now?? // signal(SIGINT, signalHandler); if (simulation) { - int num_species = (simulation->model)->number_species; - int num_reactions = (simulation->model)->number_reactions; - int num_trajectories = simulation->number_trajectories; Model &model = *(simulation->model); + int num_species = model.number_species; + int num_reactions = model.number_reactions; + int num_trajectories = simulation->number_trajectories; std::unique_ptr &species = model.species; //TauArgs tau_args = initialize(*(simulation->model),tau_tol); double increment = simulation->timeline[1] - simulation->timeline[0]; + // Initialize the hybrid solver reaction state. + // Hybrid-specific state is localized within a set of custom structs. + HybridReaction reaction_state[num_reactions]; + HybridSpecies species_state[num_species]; + + for (int rxn_i = 0; rxn_i < num_reactions; ++rxn_i) { + reaction_state[rxn_i].base_reaction = &model.reactions[rxn_i]; + } + for (int spec_i = 0; spec_i < num_species; ++spec_i) { + species_state[spec_i].base_species = &model.species[spec_i]; + } // Population/concentration state values for each species. // TODO: change back double -> hybrid_state, once we figure out how that works @@ -132,34 +161,36 @@ namespace Gillespy::TauHybrid { // This gets passed in to the integrator. UserData *data = new UserData { simulation, + species_state, + reaction_state }; // INITIALIZE INTEGRATOR STATE - // Integrator is used to integrate two variable sets separately: - // - concentrations for deterministic reactions - // - reaction offsets for stochastic reactions - // [ --- concentrations --- | --- rxn_offsets --- ] - // concentrations: bounded by [0, num_species) - // rxn_offsets: bounded by [num_species, num_species + num_reactions) - int rxn_offset_boundary = num_species + num_reactions; - - // The first half of the integration vector is used for integrating species concentrations. - // [ --- concentrations --- | ... + // Integrator is used to integrate two variable sets separately: + // - concentrations for deterministic reactions + // - reaction offsets for stochastic reactions + // [ --- concentrations --- | --- rxn_offsets --- ] + // concentrations: bounded by [0, num_species) + // rxn_offsets: bounded by [num_species, num_species + num_reactions) + int rxn_offset_boundary = num_species + num_reactions; + + // The first half of the integration vector is used for integrating species concentrations. + // [ --- concentrations --- | ... N_Vector y0 = N_VNew_Serial(rxn_offset_boundary); for (int spec_i = 0; spec_i < num_species; ++spec_i) { NV_Ith_S(y0, spec_i) = species[spec_i].initial_population; } - // The second half represents the current "randomized state" for each reaction. - // ... | --- rxn_offsets --- ] - for (int rxn_i = num_species; rxn_i < rxn_offset_boundary; ++rxn_i) { - // Represents the current "randomized state" for each reaction, used as a - // helper value to determine if/how many stochastic reactions fire. - // This gets initialized to a random negative offset, and gets "less negative" - // during the integration step. - // After each integration step, the reaction_state is used to count stochastic reactions. - NV_Ith_S(y0, rxn_i) = log(uniform(rng)); - } + // The second half represents the current "randomized state" for each reaction. + // ... | --- rxn_offsets --- ] + for (int rxn_i = num_species; rxn_i < rxn_offset_boundary; ++rxn_i) { + // Represents the current "randomized state" for each reaction, used as a + // helper value to determine if/how many stochastic reactions fire. + // This gets initialized to a random negative offset, and gets "less negative" + // during the integration step. + // After each integration step, the reaction_state is used to count stochastic reactions. + NV_Ith_S(y0, rxn_i) = log(uniform(rng)); + } // Build the ODE memory object and initialize it. // Accepts initial integrator state y0, start time t0, and RHS f. @@ -179,10 +210,10 @@ namespace Gillespy::TauHybrid { double tau_step = increment; int save_time = 0; - // Temporary array to store changes to dependent species. - // Should be 0-initialized each time it's used. - int *population_changes = new int[num_species]; - simulation->current_time = 0; + // Temporary array to store changes to dependent species. + // Should be 0-initialized each time it's used. + int *population_changes = new int[num_species]; + simulation->current_time = 0; while (simulation->current_time < simulation->end_time) { // Determine what the next time point is. // This will become current_time on the next iteration. @@ -194,12 +225,12 @@ namespace Gillespy::TauHybrid { // For stochastic reactions, integration updates the rxn_offsets vector. flag = CVode(cvode_mem, next_time, y0, &next_time, CV_NORMAL); - // "Extract" the different partitions of the vector. - // [ --- concentrations --- | --- rxn_offsets --- ] - // concentrations: bounded by [0, num_species) - // rxn_offsets: bounded by [num_species, num_species + num_reactions) - realtype *concentrations = NV_DATA_S(y0); - realtype *rxn_offsets = NV_DATA_S(y0) + num_species; + // "Extract" the different partitions of the vector. + // [ --- concentrations --- | --- rxn_offsets --- ] + // concentrations: bounded by [0, num_species) + // rxn_offsets: bounded by [num_species, num_species + num_reactions) + realtype *concentrations = NV_DATA_S(y0); + realtype *rxn_offsets = NV_DATA_S(y0) + num_species; // The newly-updated reaction_states vector may need to be reconciled now. // A positive reaction_state means reactions have potentially fired. @@ -237,8 +268,8 @@ namespace Gillespy::TauHybrid { current_state[p_i] += population_changes[p_i]; } - // "Permanently" update the rxn_state in the integrator. - rxn_offsets[rxn_i] = rxn_state; + // "Permanently" update the rxn_state in the integrator. + rxn_offsets[rxn_i] = rxn_state; } else { // Invalid population state detected; try a smaller Tau step. @@ -268,7 +299,7 @@ namespace Gillespy::TauHybrid { CVodeFree(&cvode_mem); SUNLinSolFree_SPGMR(LS); delete data; - delete[] population_changes; + delete[] population_changes; } } } @@ -280,6 +311,7 @@ namespace Gillespy::TauHybrid { */ static int f(realtype t, N_Vector y, N_Vector ydot, void *user_data) { + using namespace Gillespy::TauHybrid; // Get y(t) vector and f(t, y) vector realtype *Y = N_VGetArrayPointer(y); realtype *dydt = N_VGetArrayPointer(ydot); @@ -287,46 +319,52 @@ static int f(realtype t, N_Vector y, N_Vector ydot, void *user_data) // Extract simulation data UserData *data = static_cast(user_data); - Gillespy::TauHybrid::HybridSimulation *sim = data->my_sim; - unsigned int num_species = sim->model->number_species; - unsigned int num_reactions = sim->model->number_reactions; - - realtype *rxn_offsets = &Y[num_species]; - realtype *dydt_offsets = &dydt[num_species]; - int rxn_offset_boundary = num_species + num_reactions; + HybridSimulation *sim = data->my_sim; + HybridSpecies *species = data->species_state; + HybridReaction *reactions = data->reaciton_state; + unsigned int num_species = sim->model->number_species; + unsigned int num_reactions = sim->model->number_reactions; + + // Differentiate different regions of the input/output vectors. + // First half is for concentrations, second half is for reaction offsets. + realtype *rxn_offsets = &Y[num_species]; + realtype *dydt_offsets = &dydt[num_species]; + int rxn_offset_boundary = num_species + num_reactions; std::vector concentrations(num_species); - std::vector populations(num_species); + std::vector populations(num_species); // Populate the current ODE state into the concentrations vector. // dy/dt results are initialized to zero, and become the change in propensity. unsigned int spec_i; - for (spec_i = 0; spec_i < sim->model->number_species; ++spec_i) { + for (spec_i = 0; spec_i < num_species; ++spec_i) { populations[spec_i] = concentrations[spec_i] = Y[spec_i]; dydt[spec_i] = 0; } - // Populate the current stochastic state into the root offset vector. - // dy/dt results are initialized to zero, and become the change in offset. - unsigned int rxn_i; - for (rxn_i = 0; rxn_i < num_reactions; ++rxn_i) { - dydt_offsets[rxn_i] = 0; - } + // Populate the current stochastic state into the root offset vector. + // dy/dt results are initialized to zero, and become the change in offset. + unsigned int rxn_i; + for (rxn_i = 0; rxn_i < num_reactions; ++rxn_i) { + dydt_offsets[rxn_i] = 0; + } // Each species has a "spot" in the y and f(y,t) vector. // For each species, place the result of f(y,t) into dydt vector. int species_change; + Reaction *current_rxn; - // Process deterministic propensity state - // These updates get written directly to the integrator's concentration state + // Process deterministic propensity state + // These updates get written directly to the integrator's concentration state for (rxn_i = 0; rxn_i < num_reactions; ++rxn_i) { - // NOTE: we may need to evaluate ODE and Tau propensities separately. - // At the moment, it's unsure whether or not that's required. + current_rxn = reactions[rxn_i].base_reaction; + // NOTE: we may need to evaluate ODE and Tau propensities separately. + // At the moment, it's unsure whether or not that's required. propensity = sim->propensity_function->ODEEvaluate(rxn_i, concentrations); for (spec_i = 0; spec_i < num_species; ++spec_i) { // Use the evaluated propensity to update the concentration levels and reaction state. // Propensity is treated as positive if it's a product, negative if it's a reactant. - species_change = sim->model->reactions[rxn_i].species_change[spec_i]; + species_change = current_rxn->species_change[spec_i]; if (species_change == 0) continue; @@ -336,11 +374,11 @@ static int f(realtype t, N_Vector y, N_Vector ydot, void *user_data) dydt[spec_i] += propensity * (-1 + 2 * (species_change > 0)); } - // Process stochastic reaction state by updating the root offset for each reaction. - propensity = sim->hybrid_propensity(rxn_i, concentrations); + // Process stochastic reaction state by updating the root offset for each reaction. + propensity = sim->hybrid_propensity(rxn_i, concentrations); - // Integrate this reaction's rxn_offset forward using the propensity. - dydt_offsets[rxn_i] += rxn_offsets[rxn_i] + propensity; + // Integrate this reaction's rxn_offset forward using the propensity. + dydt_offsets[rxn_i] += rxn_offsets[rxn_i] + propensity; } return 0; From 8b77eace0bcc858f8df1f248fa3d8052351fd9f3 Mon Sep 17 00:00:00 2001 From: Matthew Dippel Date: Tue, 8 Jun 2021 12:51:48 -0400 Subject: [PATCH 49/88] change maps to vectors --- .../cpp/c_base/tau_hybrid_cpp_solver/hybridutils.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/hybridutils.c b/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/hybridutils.c index b856dea00..5a5e1e70b 100644 --- a/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/hybridutils.c +++ b/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/hybridutils.c @@ -12,10 +12,11 @@ namespace Gillespy { // Helper method to flag reactions that can be processed deterministically (continuous change) // without exceeding the user-supplied tolerance - std::set flag_det_rxns(const Model &model, - const std::map det_species, - std::map det_rxns, - std::map> dependent_species){ + std::set flag_det_rxns(const Model &model, + const std::vector det_species, + std::vector det_rxns, + std::vector> dependent_species){ + int number_rxns = model.number_reactions; for (int rxn = 0; rxn < number_rxns; ++rxn){ // start with the assumption that reaction is determinstic From a982203df1473278869acae91ec807389273bb46 Mon Sep 17 00:00:00 2001 From: Matthew Dippel Date: Tue, 8 Jun 2021 13:58:56 -0400 Subject: [PATCH 50/88] calculate means and sd with tauArgs --- .../tau_hybrid_cpp_solver/hybridutils.c | 83 ++++++++++++++----- 1 file changed, 61 insertions(+), 22 deletions(-) diff --git a/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/hybridutils.c b/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/hybridutils.c index 5a5e1e70b..8fe325cc7 100644 --- a/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/hybridutils.c +++ b/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/hybridutils.c @@ -6,8 +6,25 @@ // toggle_reactions() namespace Gillespy { - void create_diff_eqs() - { + // helper method which is used to convert reaction channels into rate rules, and rate rules into reaction channels + // as they are switched dynamically throughout the simulation based upon user-supplied tolerance + void toggle_reactions(const Model &model, + all_compiled, + det_rxns, + dependent_species, + curr_state, + det_spec, + rr_sets){ + + } + // Helper method to convert stochastic reaction descriptions into + // differential equations, used dynamically throughout the simulation + void create_diff_eqs(std::set comb, + const Model &model, + std::vector> dependent_species, + ?? rr_sets){ + + } // Helper method to flag reactions that can be processed deterministically (continuous change) // without exceeding the user-supplied tolerance @@ -45,7 +62,13 @@ namespace Gillespy { } return new_deterministic_reactions; } - void partition_species(const Model &model, const std::vector &propensity_values, std::vector curr_state, double tau_step, double current_time, std::map &det_species) + void partition_species(const Model &model, + const std::vector &propensity_values, + std::vector curr_state, + double tau_step, + double current_time, + std::map &det_species, + const TauArgs &tauArgs) { // coefficient of variance- key:species id, value: cv std::map cv; @@ -77,28 +100,44 @@ namespace Gillespy { } } // calculate means and standard deviations for dynamic-mode species involved in reactions - for (int r = 0; r < model.number_reactions; ++r) - { - for (int s = 0; s < model.number_species; ++s) - { - // access list of species by accessing the correct element of the state-change vector (of the reaction) - if (model.species[model.reactions[r].species_change[s]].user_mode == DYNAMIC) - { - // if less than 0, that means this is a reactant - if (model.reactions[r].species_change[s] < 0) - { - means[s] -= (tau_step * propensity_values[r] * model.reactions[r].species_change[s]); - sd[s] += std::pow((tau_step * propensity_values[r] * model.reactions[r].species_change[s]), 2); - } - // if greater than 0, that means this is a product - if (model.reactions[r].species_change[s] > 0) - { - means[s] += (tau_step * propensity_values[r] * model.reactions[r].species_change[s]); - sd[s] += std::pow((tau_step * propensity_values[r] * model.reactions[r].species_change[s]), 2); - } + for (int r = 0; r < model.number_reactions; ++r) { + for (int reactant = 0; reactant < tauArgs.reaction_reactants[r].size(); ++reactant) { + int reactant_ref = tauArgs.reaction_reactants[r][reactant]; + if (model.reactions[reactant_ref].user_mode == DYNAMIC) { + means[reactant_ref] -= (tau_step * propensity_values[r] * model.reactions[r].species_change[reactant_ref]); + sd[reactant_ref] += std::pow((tau_step * propensity_values[r] * model.reactions[r].species_change[reactant_ref]), 2); + } + } + for (int product = 0; product < tauArgs.products[r].size(); ++product) { + int product_ref = tauArgs.products[r][product]; + if (model.reactions[product_ref].user_mode == DYNAMIC) { + means[product_ref] -= (tau_step * propensity_values[r] * model.reactions[r].species_change[product_ref]); + sd[product_ref] += std::pow((tau_step * propensity_values[r] * model.reactions[r].species_change[product_ref]), 2); } } } + // for (int r = 0; r < model.number_reactions; ++r) + // { + // for (int s = 0; s < model.number_species; ++s) + // { + // // access list of species by accessing the correct element of the state-change vector (of the reaction) + // if (model.species[model.reactions[r].species_change[s]].user_mode == DYNAMIC) + // { + // // if less than 0, that means this is a reactant + // if (model.reactions[r].species_change[s] < 0) + // { + // means[s] -= (tau_step * propensity_values[r] * model.reactions[r].species_change[s]); + // sd[s] += std::pow((tau_step * propensity_values[r] * model.reactions[r].species_change[s]), 2); + // } + // // if greater than 0, that means this is a product + // if (model.reactions[r].species_change[s] > 0) + // { + // means[s] += (tau_step * propensity_values[r] * model.reactions[r].species_change[s]); + // sd[s] += std::pow((tau_step * propensity_values[r] * model.reactions[r].species_change[s]), 2); + // } + // } + // } + // } // calculate coefficient of variation using means and sd for (int s = 0; s < model.number_species; ++s) { From e05b3ce348c993fb09b7517e7ad2fa3f4e780bdc Mon Sep 17 00:00:00 2001 From: Josh C Date: Wed, 9 Jun 2021 11:20:37 -0400 Subject: [PATCH 51/88] Root-finding implementation - Use reaction offset state from integrator vector - Flag invalid reactions (TODO: discard invalid timesteps) - Update population values from both pure ODE and stochastic rxns --- .../TauHybridCSolver.cpp | 34 +++++++++---------- 1 file changed, 16 insertions(+), 18 deletions(-) diff --git a/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/TauHybridCSolver.cpp b/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/TauHybridCSolver.cpp index 0d4d8cad9..dccd864b6 100644 --- a/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/TauHybridCSolver.cpp +++ b/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/TauHybridCSolver.cpp @@ -235,7 +235,7 @@ namespace Gillespy::TauHybrid { // The newly-updated reaction_states vector may need to be reconciled now. // A positive reaction_state means reactions have potentially fired. // NOTE: it is possible for a population to swing negative, where a smaller Tau is needed. - for (int rxn_i = num_species; false && rxn_i < rxn_offset_boundary; ++rxn_i) { + for (int rxn_i = 0; rxn_i < num_reactions; ++rxn_i) { // Temporary variable for the reaction's state. // Does not get updated unless the changes are deemed valid. double rxn_state = rxn_offsets[rxn_i]; @@ -246,13 +246,15 @@ namespace Gillespy::TauHybrid { // Use the current reaction_state to count the number of firings. // If a negative population is detected, then the loop breaks prematurely. - while (rxn_state >= 0) { + bool invalid_state = false; + while (rxn_state >= 0 && !invalid_state) { // "Fire" a reaction by recording changes in dependent species. // If a negative value is detected, break without saving changes. for (int spec_i = 0; spec_i < num_species; ++spec_i) { - population_changes[spec_i] += model.reactions[rxn_i].species_change[spec_i]; + population_changes[spec_i] += + model.reactions[rxn_i].species_change[spec_i]; if (current_state[spec_i] + population_changes[spec_i] < 0) { - break; + invalid_state = true; } } @@ -263,21 +265,16 @@ namespace Gillespy::TauHybrid { // Positive reaction state means a negative population was detected. // Only update state with the given population changes if valid. - if (rxn_state < 0) { - for (int p_i = 0; p_i < num_species; ++p_i) { - current_state[p_i] += population_changes[p_i]; - } - - // "Permanently" update the rxn_state in the integrator. - rxn_offsets[rxn_i] = rxn_state; + if (invalid_state) { + // TODO: invalidate reaction timestep, reset integrator state, try again + continue; } - else { - // Invalid population state detected; try a smaller Tau step. - next_time = simulation->current_time; - tau_step *= 0.5; - // TODO: Reset the integrator state to the previous time step. + // "Permanently" update the rxn_state and populations. + for (int p_i = 0; p_i < num_species; ++p_i) { + current_state[p_i] += population_changes[p_i]; } + rxn_offsets[rxn_i] = rxn_state; } // Output the results for this time step. @@ -286,7 +283,8 @@ namespace Gillespy::TauHybrid { while (save_time <= next_time) { // Write each species, one at a time (from ODE solution) for (int spec_i = 0; spec_i < num_species; ++spec_i) { - simulation->trajectories_hybrid[traj][save_time][spec_i].continuous = NV_Ith_S(y0, spec_i); + current_state[spec_i] += NV_Ith_S(y0, spec_i); + simulation->trajectories_hybrid[traj][save_time][spec_i].continuous = current_state[spec_i]; } save_time += increment; } @@ -378,7 +376,7 @@ static int f(realtype t, N_Vector y, N_Vector ydot, void *user_data) propensity = sim->hybrid_propensity(rxn_i, concentrations); // Integrate this reaction's rxn_offset forward using the propensity. - dydt_offsets[rxn_i] += rxn_offsets[rxn_i] + propensity; + dydt_offsets[rxn_i] += propensity; } return 0; From 74b0327dde64bcf2a2c21cb7e1de1327c4afe5d1 Mon Sep 17 00:00:00 2001 From: Josh C Date: Wed, 9 Jun 2021 14:25:16 -0400 Subject: [PATCH 52/88] Update hybrid solver with its own Makefile target - Rename dependency files to reflect naming convention (for wildcard targets) - Add sundials dependencies to Makefile target --- gillespy2/solvers/cpp/c_base/Makefile | 9 +- .../TauHybridSimulation.cpp | 92 +++++++++++++++ ...uHybridCSolver.cpp => TauHybridSolver.cpp} | 2 +- .../{TauHybridCSolver.h => TauHybridSolver.h} | 0 .../TauHybridTemplate.cpp | 110 ----------------- .../VariableTauHybridTemplate.cpp | 111 ------------------ 6 files changed, 99 insertions(+), 225 deletions(-) create mode 100644 gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/TauHybridSimulation.cpp rename gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/{TauHybridCSolver.cpp => TauHybridSolver.cpp} (99%) rename gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/{TauHybridCSolver.h => TauHybridSolver.h} (100%) delete mode 100644 gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/TauHybridTemplate.cpp delete mode 100644 gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/VariableTauHybridTemplate.cpp diff --git a/gillespy2/solvers/cpp/c_base/Makefile b/gillespy2/solvers/cpp/c_base/Makefile index bc690ca10..a70349cd9 100644 --- a/gillespy2/solvers/cpp/c_base/Makefile +++ b/gillespy2/solvers/cpp/c_base/Makefile @@ -39,6 +39,9 @@ $(SUNOBJ_PATHS): $(SUNDIALS_OBJ)/%.o: $(SUNDIALS_SRC)/%.c $(CC) -c -o $@ $< $(CXXFLAGS) -I$(SUNDIALS_INC) sundials: $(SUNOBJ_PATHS) ; +Tau.o: $(TAU_DIR)/tau.cpp $(TAU_DIR)/tau.h + $(CC) $(CXXFLAGS) -c -o Tau.o $(TAU_DIR)/tau.cpp $(INCLUDES) + ################################# ### SOLVER ALGORITHM COMPILE #### $(OBJ_DIR)/ODESolver.o: $(ODE_SOLVER_PATH)/ODESolver.cpp @@ -67,8 +70,8 @@ $(OBJ_DIR)/SSASimulation.o: $(SSA_SOLVER_PATH)/SSASimulation.cpp $(OBJ_DIR)/TauLeapingSimulation.o: $(TAU_LEAPING_SOLVER_PATH)/TauLeapingSimulation.cpp $(CC) $(CXXFLAGS) -c -o $(OBJ_DIR)/TauLeapingSimulation.o $(TAU_LEAPING_SOLVER_PATH)/TauLeapingSimulation.cpp $(INCLUDES) -$(OBJ_DIR)/TauHybridSimulation.o: $(TAU_HYBRID_SOLVER_PATH)/TauHybridSimulation.cpp $(TAU_DIR)/Tau.cpp - $(CC) $(CXXFLAGS) -c -o $(OBJ_DIR)/TauHybridSimulation.o $(TAU_DIR)/Tau.cpp $(TAU_HYBRID_SOLVER_PATH)/TauHybridSimulation.cpp $(INCLUDES) +$(OBJ_DIR)/TauHybridSimulation.o: $(TAU_HYBRID_SOLVER_PATH)/TauHybridSimulation.cpp + $(CC) $(CXXFLAGS) -c -o $(OBJ_DIR)/TauHybridSimulation.o $(TAU_HYBRID_SOLVER_PATH)/TauHybridSimulation.cpp $(INCLUDES) %Simulation.o: $(OBJ_DIR)/%Simulation.o ; @@ -96,7 +99,7 @@ tau_leap: TauLeapingSimulation.o TauLeapingSolver.o model $(TEMPLATE_CPP) $(CC) $(COMPILATION_ARGS) $(OBJ_DIR)/TauLeapingSimulation.o $(OBJ_DIR)/TauLeapingSolver.o hybrid: TauHybridSimulation.o TauHybridSolver.o model sundials $(TEMPLATE_CPP) - $(CC) $(COMPILATION_ARGS) $(OBJ_DIR)/TauHybridSimulation.o $(OBJ_DIR)/TauHybridSolver.o + $(CC) $(COMPILATION_ARGS) $(SUNOBJ_PATHS) $(OBJ_DIR)/TauHybridSimulation.o $(OBJ_DIR)/TauHybridSolver.o clean: rm -rf $(OUTPUT_DIR)/*.out diff --git a/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/TauHybridSimulation.cpp b/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/TauHybridSimulation.cpp new file mode 100644 index 000000000..dc1bae433 --- /dev/null +++ b/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/TauHybridSimulation.cpp @@ -0,0 +1,92 @@ +#include +#include +#include +#include +#include +#include +#include "TauHybridSolver.h" +#include "template.h" +using namespace Gillespy; + +//Default values, replaced with command line args +unsigned int number_trajectories = 0; +unsigned int number_timesteps = 0; +int random_seed = 0; +double end_time = 100.0; +bool seed_time = true; +double increment = 0; +double tau_tol = 0.05; +class PropensityFunction : public IPropensityFunction{ +public: + + double ODEEvaluate(int reaction_number, const std::vector &S){ + return map_ode_propensity(reaction_number, S); + } + double TauEvaluate(unsigned int reaction_number, const std::vector &S){return 1.0;} + double evaluate(unsigned int reaction_number, unsigned int* S){return 1.0;} +}; + +double Gillespy::TauHybrid::HybridSimulation::hybrid_propensity( + int reaction_id, + std::vector &S) +{ + return map_ode_propensity(reaction_id, S); +} + +int main(int argc, char* argv[]){ + //Parse command line arguments + std :: string arg; + for(int i = 1; i < argc - 1; i++){ + arg = argv[i]; + if(argc > i+1 && arg.size() > 1 && arg[0] == '-'){ + std :: stringstream arg_stream(argv[i+1]); + switch(arg[1]){ + case 's': + arg_stream >> random_seed; + seed_time = false; + break; + case 'e': + arg_stream >> end_time; + break; + case 'i': + map_variable_populations(arg_stream); + break; + case 'p': + map_variable_parameters(arg_stream); + break; + case 't': + if(arg[2] == 'r'){ + arg_stream >> number_trajectories; + }else if(arg[2] == 'i'){ + arg_stream >> number_timesteps; + }else if (arg[2] == 'a'){ // '-tau_tol' + arg_stream >> tau_tol; + } + break; + } + } + } + + Model model(species_names, species_populations, reaction_names); + add_reactions(model); + + if(seed_time){ + random_seed = time(NULL); + } + IPropensityFunction *propFun = new PropensityFunction(); + //Simulation INIT + TauHybrid::HybridSimulation simulation; + simulation.type = HYBRID; + simulation.model = &model; + simulation.end_time = end_time; + simulation.random_seed = random_seed; + simulation.number_timesteps = number_timesteps; + simulation.number_trajectories = number_trajectories; + simulation.propensity_function = propFun; + TauHybrid::simulation_hybrid_init(simulation); + // Perform ODE // + TauHybrid::TauHybridCSolver(&simulation, tau_tol); + simulation.output_hybrid_results(std :: cout); + delete propFun; + return 0; +} diff --git a/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/TauHybridCSolver.cpp b/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/TauHybridSolver.cpp similarity index 99% rename from gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/TauHybridCSolver.cpp rename to gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/TauHybridSolver.cpp index dccd864b6..42fa2c443 100644 --- a/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/TauHybridCSolver.cpp +++ b/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/TauHybridSolver.cpp @@ -7,7 +7,7 @@ #include "cvode_spils.h" // access to CVSpils interface #include "sundials_types.h" // defs. of realtype, sunindextype #include "sundials_math.h" // contains the macros ABS, SUNSQR, EXP -#include "TauHybridCSolver.h" +#include "TauHybridSolver.h" #include "HybridModel.h" // #include "statistics.h" #include "tau.h" diff --git a/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/TauHybridCSolver.h b/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/TauHybridSolver.h similarity index 100% rename from gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/TauHybridCSolver.h rename to gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/TauHybridSolver.h diff --git a/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/TauHybridTemplate.cpp b/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/TauHybridTemplate.cpp deleted file mode 100644 index f5daa182f..000000000 --- a/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/TauHybridTemplate.cpp +++ /dev/null @@ -1,110 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include "TauHybridCSolver.h" -using namespace Gillespy; - -//Default values, replaced with command line args -unsigned int number_trajectories = 0; -unsigned int number_timesteps = 0; -int random_seed = 0; -double end_time = 100.0; -bool seed_time = true; -double increment = 0; - -//Default constants/variables -__DEFINE_VARIABLES__ - -class PropensityFunction : public IPropensityFunction{ -public: - -double ODEEvaluate(int reaction_number, const std::vector &S){ - switch(reaction_number){ - -__DEFINE_PROPENSITY__ - - default: //Error - return -1; - } - } - double TauEvaluate(unsigned int reaction_number, const std::vector &S){return 1.0;} - double evaluate(unsigned int reaction_number, unsigned int* S){return 1.0;} -}; - -double Gillespy::TauHybrid::HybridSimulation::hybrid_propensity( - int reaction_id, - std::vector &S) -{ - switch (reaction_id) { -__DEFINE_PROPENSITY__ - - default: - return -1.0; - } -} - -int main(int argc, char* argv[]){ - std :: vector species_names(s_names, s_names + sizeof(s_names)/sizeof(std :: string)); - std :: vector species_populations(populations, populations + sizeof(populations)/sizeof(populations[0])); - std :: vector reaction_names(r_names, r_names + sizeof(r_names)/sizeof(std :: string)); - - Model model(species_names, species_populations, reaction_names); - - //Begin reaction species changes -__DEFINE_REACTIONS_ - //End reaction species changes - model.update_affected_reactions(); - - //Parse command line arguments - std :: string arg; - for(int i = 1; i < argc - 1; i++){ - arg = argv[i]; - if(argc > i+1 && arg.size() > 1 && arg[0] == '-'){ - std :: stringstream arg_stream(argv[i+1]); - switch(arg[1]){ - case 's': - arg_stream >> random_seed; - seed_time = false; - break; - case 'e': - arg_stream >> end_time; - break; - case 'i': - arg_stream >> increment; - break; - case 't': - if(arg[2] == 'r'){ - arg_stream >> number_trajectories; - }else if(arg[2] == 'i'){ - arg_stream >> number_timesteps; - } - break; - } - } - } - - if(seed_time){ - random_seed = time(NULL); - } - IPropensityFunction *propFun = new PropensityFunction(); - //Simulation INIT - TauHybrid::HybridSimulation simulation; - Model* modelptr; - modelptr = &model; - simulation.type = HYBRID; - simulation.model = modelptr; - simulation.end_time = end_time; - simulation.random_seed = random_seed; - simulation.number_timesteps = number_timesteps; - simulation.number_trajectories = number_trajectories; - simulation.propensity_function = propFun; - TauHybrid::simulation_hybrid_init(simulation); - // Perform ODE // - TauHybrid::TauHybridCSolver(&simulation, 0.05); - simulation.output_hybrid_results(std :: cout); - delete propFun; - return 0; -} \ No newline at end of file diff --git a/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/VariableTauHybridTemplate.cpp b/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/VariableTauHybridTemplate.cpp deleted file mode 100644 index a6163de3a..000000000 --- a/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/VariableTauHybridTemplate.cpp +++ /dev/null @@ -1,111 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include "model.h" -#include "ODECSolver.h" -using namespace Gillespy; - -//Default values, replaced with command line args -unsigned int number_trajectories = 0; -unsigned int number_timesteps = 0; -int random_seed = 0; -double end_time = 100.0; -bool seed_time = true; -double increment = 0; - -//Default constants/variables -__DEFINE_VARIABLES__ - -class PropensityFunction : public IPropensityFunction{ -public: - -double ODEEvaluate(int reaction_number, const std::vector &S){ - switch(reaction_number){ - -__DEFINE_PROPENSITY__ - - default: //Error - return -1; - } - } - double TauEvaluate(unsigned int reaction_number, const std::vector &S){return 1.0;} - double evaluate(unsigned int reaction_number, unsigned int* S){return 1.0;} -}; - -int main(int argc, char* argv[]){ - //Parse command line arguments - std :: string arg; - for(int i = 1; i < argc - 1; i++){ - arg = argv[i]; - if(argc > i+1 && arg.size() > 1 && arg[0] == '-'){ - std :: stringstream arg_stream(argv[i+1]); - switch(arg[1]){ - case 'i': - if (arg[3] == 'c'){ - arg_stream >> increment; - } - else if(arg[3] == 'i'){ - for(int j = 0; j < int(sizeof(populations)); j++){ - arg_stream >> populations[j]; - } - } - break; - case 'p': -__DEFINE_PARAMETER_UPDATES__ - break; - case 's': - arg_stream >> random_seed; - seed_time = false; - break; - case 'e': - arg_stream >> end_time; - break; - case 't': - if(arg[2] == 'r'){ - arg_stream >> number_trajectories; - }else if(arg[2] == 'i'){ - arg_stream >> number_timesteps; - } - break; - } - } - } - - - - std :: vector species_names(s_names, s_names + sizeof(s_names)/sizeof(std :: string)); - std :: vector species_populations(populations, populations + sizeof(populations)/sizeof(populations[0])); - std :: vector reaction_names(r_names, r_names + sizeof(r_names)/sizeof(std :: string)); - - Model model(species_names, species_populations, reaction_names); - - //Begin reaction species changes -__DEFINE_REACTIONS_ - //End reaction species changes - model.update_affected_reactions(); - - if(seed_time){ - random_seed = time(NULL); - } - IPropensityFunction *propFun = new PropensityFunction(); - //Simulation INIT - Simulation simulation; - Model* modelptr; - modelptr = &model; - simulation.type = HYBRID; - simulation.model = modelptr; - simulation.end_time = end_time; - simulation.random_seed = random_seed; - simulation.number_timesteps = number_timesteps; - simulation.number_trajectories = number_trajectories; - simulation.propensity_function = propFun; - simulationINIT(&model, simulation); - // Perform ODE // - ODESolver(&simulation,increment); - simulation.output_results_buffer(std :: cout); - delete propFun; - return 0; -} \ No newline at end of file From d564439cbe3459e8cf9c73bee7c9a3574c807275 Mon Sep 17 00:00:00 2001 From: Josh C Date: Wed, 9 Jun 2021 15:17:49 -0400 Subject: [PATCH 53/88] Debug targets for makefile - `build` and `run` targets for running w/o debug - Set `GPY_ARGS` and `SOLVER` environment variables - `debug` and `gdb` targets for debugging --- gillespy2/solvers/cpp/c_base/Makefile | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/gillespy2/solvers/cpp/c_base/Makefile b/gillespy2/solvers/cpp/c_base/Makefile index a70349cd9..f226daffc 100644 --- a/gillespy2/solvers/cpp/c_base/Makefile +++ b/gillespy2/solvers/cpp/c_base/Makefile @@ -1,6 +1,7 @@ ### Compiler configuration/settings ### CC=g++ CXXFLAGS = -std=c++14 -Wall -O3 +GPY_SOLVER ?= ssa ####################################### ### Input directories ### @@ -20,6 +21,8 @@ TAU_HYBRID_SOLVER_PATH := $(CBASE_DIR)/tau_hybrid_cpp_solver OBJ_DIR ?= $(CBASE_DIR) OUTPUT_DIR ?= $(CBASE_DIR) OUTPUT_FILE ?= $(OUTPUT_DIR)/Simulation.out +EXE_CMD ?= ./ +EXE_ARGS ?= -trajectories 1 -end 10 -timesteps 11 -increment 1 SUNDIALS_OBJ ?= $(OBJ_DIR) INCLUDES := -I$(CBASE_DIR) -I$(SUNDIALS_INC) -I$(TEMPLATE_DIR) -I$(TAU_DIR) ########################## @@ -87,7 +90,7 @@ prebuild: prebuild_solvers prebuild_simulations sundials ; ########################## ### FINAL COMPILATIONS ### -COMPILATION_ARGS := $(CXXFLAGS) -o $(OUTPUT_FILE) $(TEMPLATE_CPP) $(OBJ_DIR)/model.o $(INCLUDES) +COMPILATION_ARGS = $(CXXFLAGS) -o $(OUTPUT_FILE) $(TEMPLATE_CPP) $(OBJ_DIR)/model.o $(INCLUDES) ode: ODESimulation.o ODESolver.o sundials model $(TEMPLATE_CPP) $(CC) $(COMPILATION_ARGS) $(SUNOBJ_PATHS) $(OBJ_DIR)/ODESimulation.o $(OBJ_DIR)/ODESolver.o @@ -101,6 +104,15 @@ tau_leap: TauLeapingSimulation.o TauLeapingSolver.o model $(TEMPLATE_CPP) hybrid: TauHybridSimulation.o TauHybridSolver.o model sundials $(TEMPLATE_CPP) $(CC) $(COMPILATION_ARGS) $(SUNOBJ_PATHS) $(OBJ_DIR)/TauHybridSimulation.o $(OBJ_DIR)/TauHybridSolver.o +build: $(GPY_SOLVER); +debug: CXXFLAGS = -std=c++14 -Wall -g +debug: build; + +run: build + ./$(OUTPUT_FILE) $(EXE_ARGS) +gdb: debug + gdb --args $(OUTPUT_FILE) $(EXE_ARGS) + clean: rm -rf $(OUTPUT_DIR)/*.out From 70aa132ded13c1d684de056e46574107f1c786d4 Mon Sep 17 00:00:00 2001 From: Josh C Date: Wed, 9 Jun 2021 15:51:00 -0400 Subject: [PATCH 54/88] Propensity updates during integration - Add `propensities` vector to user data struct - Add tau selection step to hybrid solver - Update Python solver class to reflect new build system --- gillespy2/solvers/cpp/c_base/Makefile | 4 +- .../tau_hybrid_cpp_solver/TauHybridSolver.cpp | 30 +- gillespy2/solvers/cpp/tau_hybrid_c_solver.py | 310 ++++-------------- 3 files changed, 96 insertions(+), 248 deletions(-) diff --git a/gillespy2/solvers/cpp/c_base/Makefile b/gillespy2/solvers/cpp/c_base/Makefile index f226daffc..bc34efc9b 100644 --- a/gillespy2/solvers/cpp/c_base/Makefile +++ b/gillespy2/solvers/cpp/c_base/Makefile @@ -101,8 +101,8 @@ ssa: SSASimulation.o SSASolver.o model $(TEMPLATE_CPP) tau_leap: TauLeapingSimulation.o TauLeapingSolver.o model $(TEMPLATE_CPP) $(CC) $(COMPILATION_ARGS) $(OBJ_DIR)/TauLeapingSimulation.o $(OBJ_DIR)/TauLeapingSolver.o -hybrid: TauHybridSimulation.o TauHybridSolver.o model sundials $(TEMPLATE_CPP) - $(CC) $(COMPILATION_ARGS) $(SUNOBJ_PATHS) $(OBJ_DIR)/TauHybridSimulation.o $(OBJ_DIR)/TauHybridSolver.o +hybrid: TauHybridSimulation.o TauHybridSolver.o model sundials $(TAU_DIR)/tau.cpp $(TEMPLATE_CPP) + $(CC) $(COMPILATION_ARGS) $(SUNOBJ_PATHS) $(TAU_DIR)/tau.cpp $(OBJ_DIR)/TauHybridSimulation.o $(OBJ_DIR)/TauHybridSolver.o build: $(GPY_SOLVER); debug: CXXFLAGS = -std=c++14 -Wall -g diff --git a/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/TauHybridSolver.cpp b/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/TauHybridSolver.cpp index 42fa2c443..d7ead8884 100644 --- a/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/TauHybridSolver.cpp +++ b/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/TauHybridSolver.cpp @@ -21,6 +21,7 @@ struct UserData { Gillespy::TauHybrid::HybridSimulation *my_sim; Gillespy::TauHybrid::HybridSpecies *species_state; Gillespy::TauHybrid::HybridReaction *reaciton_state; + std::vector propensities; }; struct IntegratorOptions{ // CVODE constants returned if bad output, or success output. @@ -69,7 +70,7 @@ namespace Gillespy::TauHybrid { os << timeline[j] << ','; for (int k = 0; k < model->number_species; k++) { - os << trajectories_hybrid[i][j][k].continuous << ','; + os << trajectories_hybrid[i][j][k].discrete << ','; } } @@ -121,6 +122,9 @@ namespace Gillespy::TauHybrid { HybridReaction reaction_state[num_reactions]; HybridSpecies species_state[num_species]; + // Tau selector initialization. Used to select a valid tau step. + TauArgs tau_args = initialize(model, tau_tol); + for (int rxn_i = 0; rxn_i < num_reactions; ++rxn_i) { reaction_state[rxn_i].base_reaction = &model.reactions[rxn_i]; } @@ -130,7 +134,7 @@ namespace Gillespy::TauHybrid { // Population/concentration state values for each species. // TODO: change back double -> hybrid_state, once we figure out how that works - std::vector current_state(num_species); + std::vector current_state(num_species); //initialize propensity_values to 0 for each species std::vector propensity_values(num_reactions); @@ -162,7 +166,8 @@ namespace Gillespy::TauHybrid { UserData *data = new UserData { simulation, species_state, - reaction_state + reaction_state, + propensity_values }; // INITIALIZE INTEGRATOR STATE @@ -207,7 +212,7 @@ namespace Gillespy::TauHybrid { // SIMULATION STEP LOOP double next_time; - double tau_step = increment; + double tau_step = 0.0; int save_time = 0; // Temporary array to store changes to dependent species. @@ -215,6 +220,17 @@ namespace Gillespy::TauHybrid { int *population_changes = new int[num_species]; simulation->current_time = 0; while (simulation->current_time < simulation->end_time) { + // Expected tau step is determined. + tau_step = select( + model, + tau_args, + tau_tol, + simulation->current_time, + save_time, + propensity_values, + current_state + ); + // Determine what the next time point is. // This will become current_time on the next iteration. // If a retry with a smaller tau_step is deemed necessary, this will change. @@ -283,8 +299,8 @@ namespace Gillespy::TauHybrid { while (save_time <= next_time) { // Write each species, one at a time (from ODE solution) for (int spec_i = 0; spec_i < num_species; ++spec_i) { - current_state[spec_i] += NV_Ith_S(y0, spec_i); - simulation->trajectories_hybrid[traj][save_time][spec_i].continuous = current_state[spec_i]; + // current_state[spec_i] += NV_Ith_S(y0, spec_i); + simulation->trajectories_hybrid[traj][save_time][spec_i].discrete = current_state[spec_i]; } save_time += increment; } @@ -320,6 +336,7 @@ static int f(realtype t, N_Vector y, N_Vector ydot, void *user_data) HybridSimulation *sim = data->my_sim; HybridSpecies *species = data->species_state; HybridReaction *reactions = data->reaciton_state; + std::vector propensities = data->propensities; unsigned int num_species = sim->model->number_species; unsigned int num_reactions = sim->model->number_reactions; @@ -374,6 +391,7 @@ static int f(realtype t, N_Vector y, N_Vector ydot, void *user_data) // Process stochastic reaction state by updating the root offset for each reaction. propensity = sim->hybrid_propensity(rxn_i, concentrations); + propensities[rxn_i] = propensity; // Integrate this reaction's rxn_offset forward using the propensity. dydt_offsets[rxn_i] += propensity; diff --git a/gillespy2/solvers/cpp/tau_hybrid_c_solver.py b/gillespy2/solvers/cpp/tau_hybrid_c_solver.py index 6f77c09a3..51567e096 100644 --- a/gillespy2/solvers/cpp/tau_hybrid_c_solver.py +++ b/gillespy2/solvers/cpp/tau_hybrid_c_solver.py @@ -1,254 +1,84 @@ -import gillespy2 -from gillespy2.core import gillespyError, GillesPySolver, log +from gillespy2.solvers.cpp.c_decoder import BasicSimDecoder from gillespy2.solvers.utilities import solverutils as cutils -import signal -import time # for solver timeout implementation -import os # for getting directories for C++ files -import shutil # for deleting/copying files -import subprocess # For calling make and executing c solver -import tempfile # for temporary directories - -GILLESPY_PATH = os.path.dirname(os.path.abspath(__file__)) -GILLESPY_CPP_TAU_HYBRID_DIR = os.path.join(GILLESPY_PATH, 'c_base/tau_hybrid_cpp_solver') -MAKE_FILE = os.path.dirname(os.path.abspath( - __file__)) + '/c_base/tau_hybrid_cpp_solver/makefile' -SUNDIALS_DIR = os.path.join(GILLESPY_PATH, 'c_base/Sundials') -CBASE_DIR = os.path.join(GILLESPY_PATH, 'c_base/') - - -class TauHybridCSolver(GillesPySolver): - name = "TauHybridCSolver" - """TODO""" - - def __init__(self, model=None, output_directory=None, delete_directory=True, resume=None, variable=False): - super(TauHybridCSolver, self).__init__() - self.__compiled = False - self.delete_directory = False - self.model = model - self.resume = resume - self.variable = variable - if self.model is not None: - # Create constant, ordered lists for reactions/species/ - self.species_mappings = self.model.sanitized_species_names() - self.species = list(self.species_mappings.keys()) - self.parameter_mappings = self.model.sanitized_parameter_names() - self.parameters = list(self.parameter_mappings.keys()) - self.reactions = list(self.model.listOfReactions.keys()) - - if isinstance(output_directory, str): - output_directory = os.path.abspath(output_directory) - - if isinstance(output_directory, str): - if not os.path.isfile(output_directory): - self.output_directory = output_directory - self.delete_directory = delete_directory - if not os.path.isdir(output_directory): - os.makedirs(self.output_directory) - else: - raise gillespyError.DirectoryError( - "File exists with the same path as directory.") - else: - self.temporary_directory = tempfile.TemporaryDirectory() - self.output_directory = self.temporary_directory.name - - if not os.path.isdir(self.output_directory): - raise gillespyError.DirectoryError("Errors encountered while setting up directory for Solver C++ files." - ) - self.__write_template() - self.__compile() - - def __del__(self): - if self.delete_directory and os.path.isdir(self.output_directory): - shutil.rmtree(self.output_directory) - - def __write_template(self): - # Open up template file for reading. +from gillespy2.core import GillesPySolver, gillespyError, Model - if self.variable: - template_file = "VariableTauHybridTemplate.cpp" - else: - template_file = "TauHybridTemplate.cpp" - - with open(os.path.join(GILLESPY_CPP_TAU_HYBRID_DIR, template_file), 'r') as template: - # Write simulation C++ file. - template_keyword = "__DEFINE_" - # Use same lists of model's species and reactions to maintain order - with open(os.path.join(self.output_directory, 'TauHybridSimulation.cpp'), 'w') as outfile: - for line in template: - if line.startswith(template_keyword): - line = line[len(template_keyword):] - if line.startswith("VARIABLES"): - cutils.write_variables(outfile, self.model, self.reactions, self.species, - self.parameter_mappings, self.resume, variable=self.variable) - if line.startswith("PROPENSITY"): - cutils.write_propensity(outfile, self.model, self.species_mappings, self.parameter_mappings, - self.reactions) - if line.startswith("REACTIONS"): - cutils.write_reactions( - outfile, self.model, self.reactions, self.species) - if self.variable: - if line.startswith("PARAMETER_UPDATES"): - cutils.update_parameters( - outfile, self.parameters, self.parameter_mappings) - else: - outfile.write(line) - - def __compile(self): - # Use makefile. - if self.resume: - if self.resume[0].model != self.model: - raise gillespyError.ModelError( - 'When resuming, one must not alter the model being resumed.') - try: - cmd = ["make", "-C", self.output_directory, '-f', MAKE_FILE, - 'TauHybridSimulation', 'GILLESPY_CPP_TAU_HYBRID_DIR=' + - GILLESPY_CPP_TAU_HYBRID_DIR, 'CBASE_DIR='+CBASE_DIR, - 'SUNDIALS_DIR='+SUNDIALS_DIR] - built = subprocess.run( - cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE) - except KeyboardInterrupt: - log.warning( - "Solver has been interrupted during compile time, unexpected behavior may occur.") - - if built.returncode == 0: - self.__compiled = True - else: - raise gillespyError.BuildError("Error encountered while compiling file:\nReturn code: " - "{0}.\nError:\n{1}\n{2}\n".format(built.returncode, - built.stdout.decode( - 'utf-8'), - built.stderr.decode('utf-8'))) +from .c_solver import CSolver, SimulationReturnCode + +class TauHybridCSolver(GillesPySolver, CSolver): + name = "TauHybridCSolver" + target = "hybrid" def get_solver_settings(self): """ :return: Tuple of strings, denoting all keyword argument for this solvers run() method. """ - return ('model', 't', 'number_of_trajectories', 'timeout', 'increment', 'seed', 'debug', 'profile', 'variables') - - def run(self=None, model=None, t=20, number_of_trajectories=1, timeout=0, - increment=0.05, seed=None, debug=False, profile=False, variables={}, resume=None, **kwargs): + return ('model', 't', 'number_of_trajectories', 'timeout', 'increment', 'seed', 'debug', 'profile') - pause = False - - if resume is not None: - if t < resume['time'][-1]: - raise gillespyError.ExecutionError( - "'t' must be greater than previous simulations end time, or set in the run() method as the " - "simulations next end time") + def run(self=None, model: Model = None, t: int = 20, number_of_trajectories: int = 1, timeout: int = 0, + increment: int = 0.05, seed: int = None, debug: bool = False, profile: bool = False, variables={}, + resume=None, tau_step: int = .03, tau_tol=0.03, **kwargs): if self is None or self.model is None: self = TauHybridCSolver(model, resume=resume) - if len(kwargs) > 0: - for key in kwargs: - log.warning( - 'Unsupported keyword argument to {0} solver: {1}'.format(self.name, key)) + # Validate parameters prior to running the model. + self._validate_type(variables, dict, "'variables' argument must be a dictionary.") + self._validate_variables_in_set(variables, self.species + self.parameters) + self._validate_resume(t, resume) + self._validate_kwargs(**kwargs) + self._validate_sbml_features({ + "Rate Rules": len(model.listOfRateRules), + "Assignment Rules": len(model.listOfAssignmentRules), + "Events": len(model.listOfEvents), + "Function Definitions": len(model.listOfFunctionDefinitions) + }) + + if resume is not None: + t = abs(t - int(resume["time"][-1])) - unsupported_sbml_features = { - 'Rate Rules': len(model.listOfRateRules), - 'Assignment Rules': len(model.listOfAssignmentRules), - 'Events': len(model.listOfEvents), - 'Function Definitions': len(model.listOfFunctionDefinitions) + number_timesteps = int(round(t / increment + 1)) + + args = { + "trajectories": number_of_trajectories, + "timesteps": number_timesteps, + "tau_step": tau_step, + "tau_tol": tau_tol, + "end": t } - detected_features = [] - for feature, count in unsupported_sbml_features.items(): - if count: - detected_features.append(feature) - - if len(detected_features): - raise gillespyError.ModelError( - 'Could not run Model. SBML Feature: {} not supported by TauHybridCSolver.'.format(detected_features)) - - if not isinstance(variables, dict): - raise gillespyError.SimulationError( - 'argument to variables must be a dictionary.') - for v in variables.keys(): - if v not in self.species+self.parameters: - raise gillespyError.SimulationError('Argument to variable "{}" \ - is not a valid variable. Variables must be model species or parameters.'.format(v)) - - if self.__compiled: - if self.variable: # Is a variable simulation - populations = cutils.update_species_init_values( - model.listOfSpecies, self.species, variables, resume) - parameter_values = cutils.change_param_values( - model.listOfParameters, self.parameters, model.volume, variables) - - self.simulation_data = None - if resume is not None: - t = abs(t - int(resume['time'][-1])) - - number_timesteps = int(round(t/increment + 1)) - # Execute simulation. - - args = [os.path.join(self.output_directory, 'TauHybridSimulation'), - '-trajectories', str(number_of_trajectories), - '-timesteps', str(number_timesteps), - '-end', str(t), '-increment', str(increment)] - - if self.variable: - args.extend(['-initial_values', populations, - '-parameters', parameter_values]) - - if seed is not None: - if isinstance(seed, int): - args.append('-seed') - args.append(str(seed)) - else: - seed_int = int(seed) - if seed_int > 0: - args.append('-seed') - args.append(str(seed_int)) - else: - raise gillespyError.ModelError( - "seed must be a positive integer") - - # begin subprocess c simulation with timeout (default timeout=0 will not timeout) - with subprocess.Popen(args, stdout=subprocess.PIPE, start_new_session=True) as simulation: - try: - if timeout > 0: - stdout, stderr = simulation.communicate( - timeout=timeout) - else: - stdout, stderr = simulation.communicate() - return_code = simulation.wait() - except KeyboardInterrupt: - # send signal to the process group - os.killpg(simulation.pid, signal.SIGINT) - stdout, stderr = simulation.communicate() - pause = True - return_code = 33 - except subprocess.TimeoutExpired: - # send signal to the process group - os.killpg(simulation.pid, signal.SIGINT) - stdout, stderr = simulation.communicate() - pause = True - return_code = 33 - # Decode from byte, split by comma into array - stdout = stdout.decode('utf-8').split(',') - # Parse/return results - - if return_code in [0, 33]: - trajectory_base, timeStopped = cutils.parse_binary_output(number_of_trajectories, number_timesteps, - len(model.listOfSpecies), stdout, pause=pause) - if model.tspan[2] - model.tspan[1] == 1: - timeStopped = int(timeStopped) - - # Format results - self.simulation_data = [] - for trajectory in range(number_of_trajectories): - data = {'time': trajectory_base[trajectory, :, 0]} - for i in range(len(self.species)): - data[self.species[i]] = trajectory_base[trajectory, :, i + 1] - - self.simulation_data.append(data) - else: - raise gillespyError.ExecutionError("Error encountered while running simulation C++ file:" - "\nReturn code: {0}.\nError:\n{1}\n". - format(simulation.returncode, simulation.stderr)) - - if resume is not None or timeStopped != 0: - self.simulation_data = cutils.c_solver_resume( - timeStopped, self.simulation_data, t, resume=resume) - - return self.simulation_data, return_code + + if self.variable: + populations = cutils.update_species_init_values(model.listOfSpecies, self.species, variables, resume) + parameter_values = cutils.change_param_values(model.listOfParameters, self.parameters, model.volume, variables) + + args.update({ + "initial_values": populations, + "parameters": parameter_values + }) + + seed = self._validate_seed(seed) + if seed is not None: + args.update({ + "seed": seed + }) + + + args = self._make_args(args) + decoder = BasicSimDecoder.create_default(number_of_trajectories, number_timesteps, len(self.model.listOfSpecies)) + + sim_exec = self._build(model, self.target, self.variable, False) + sim_status = self._run(sim_exec, args, decoder, timeout) + + if sim_status == SimulationReturnCode.FAILED: + raise gillespyError.ExecutionError("Error encountered while running simulation C++ file:\n" + f"Return code: {int(sim_status)}.\n") + + trajectories, time_stopped = decoder.get_output() + + simulation_data = self._format_output(trajectories) + if sim_status == SimulationReturnCode.PAUSED: + simulation_data = self._make_resume_data(time_stopped, simulation_data, t) + if resume is not None: + simulation_data = self._update_resume_data(resume, simulation_data, time_stopped) + self.simulation_data = simulation_data + + return simulation_data, int(sim_status) From c3348925143dcc4775815bc06cd8006597d0847e Mon Sep 17 00:00:00 2001 From: Josh C Date: Wed, 9 Jun 2021 16:43:53 -0400 Subject: [PATCH 55/88] Update population values in integrator - Gets roughly "accurate" output, minus discarded reactions --- gillespy2/solvers/cpp/c_base/Makefile | 2 +- .../tau_hybrid_cpp_solver/TauHybridSolver.cpp | 68 +++++++++++-------- 2 files changed, 42 insertions(+), 28 deletions(-) diff --git a/gillespy2/solvers/cpp/c_base/Makefile b/gillespy2/solvers/cpp/c_base/Makefile index bc34efc9b..638619475 100644 --- a/gillespy2/solvers/cpp/c_base/Makefile +++ b/gillespy2/solvers/cpp/c_base/Makefile @@ -106,7 +106,7 @@ hybrid: TauHybridSimulation.o TauHybridSolver.o model sundials $(TAU_DIR)/tau.cp build: $(GPY_SOLVER); debug: CXXFLAGS = -std=c++14 -Wall -g -debug: build; +debug: clean_all build; run: build ./$(OUTPUT_FILE) $(EXE_ARGS) diff --git a/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/TauHybridSolver.cpp b/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/TauHybridSolver.cpp index d7ead8884..f6e3eea37 100644 --- a/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/TauHybridSolver.cpp +++ b/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/TauHybridSolver.cpp @@ -20,8 +20,10 @@ static int f(realtype t, N_Vector y, N_Vector y_dot, void *user_data); // forwar struct UserData { Gillespy::TauHybrid::HybridSimulation *my_sim; Gillespy::TauHybrid::HybridSpecies *species_state; - Gillespy::TauHybrid::HybridReaction *reaciton_state; - std::vector propensities; + Gillespy::TauHybrid::HybridReaction *reaction_state; + std::vector concentrations; + std::vector populations; + std::vector propensities; }; struct IntegratorOptions{ // CVODE constants returned if bad output, or success output. @@ -122,8 +124,8 @@ namespace Gillespy::TauHybrid { HybridReaction reaction_state[num_reactions]; HybridSpecies species_state[num_species]; - // Tau selector initialization. Used to select a valid tau step. - TauArgs tau_args = initialize(model, tau_tol); + // Tau selector initialization. Used to select a valid tau step. + TauArgs tau_args = initialize(model, tau_tol); for (int rxn_i = 0; rxn_i < num_reactions; ++rxn_i) { reaction_state[rxn_i].base_reaction = &model.reactions[rxn_i]; @@ -135,6 +137,10 @@ namespace Gillespy::TauHybrid { // Population/concentration state values for each species. // TODO: change back double -> hybrid_state, once we figure out how that works std::vector current_state(num_species); + // Populations and concentrations vectors are used by the integrator. + // Kept distinct so that deterministic and stochastic propensities may be evaluated separately. + std::vector populations(num_species); + std::vector concentrations(num_species); //initialize propensity_values to 0 for each species std::vector propensity_values(num_reactions); @@ -158,7 +164,10 @@ namespace Gillespy::TauHybrid { // Initialize the species population for the trajectory. for (int spec_i = 0; spec_i < num_species; ++spec_i) { - current_state[spec_i] = species[spec_i].initial_population; + populations[spec_i] + = concentrations[spec_i] + = current_state[spec_i] + = species[spec_i].initial_population; } // Struct acts as container for simulation state. @@ -167,7 +176,9 @@ namespace Gillespy::TauHybrid { simulation, species_state, reaction_state, - propensity_values + concentrations, + populations, + propensity_values }; // INITIALIZE INTEGRATOR STATE @@ -220,16 +231,16 @@ namespace Gillespy::TauHybrid { int *population_changes = new int[num_species]; simulation->current_time = 0; while (simulation->current_time < simulation->end_time) { - // Expected tau step is determined. - tau_step = select( - model, - tau_args, - tau_tol, - simulation->current_time, - save_time, - propensity_values, - current_state - ); + // Expected tau step is determined. + tau_step = select( + model, + tau_args, + tau_tol, + simulation->current_time, + save_time, + propensity_values, + populations + ); // Determine what the next time point is. // This will become current_time on the next iteration. @@ -283,6 +294,7 @@ namespace Gillespy::TauHybrid { // Only update state with the given population changes if valid. if (invalid_state) { // TODO: invalidate reaction timestep, reset integrator state, try again + std::cerr << "Integration step failed; RIP" << std::endl; continue; } @@ -329,14 +341,18 @@ static int f(realtype t, N_Vector y, N_Vector ydot, void *user_data) // Get y(t) vector and f(t, y) vector realtype *Y = N_VGetArrayPointer(y); realtype *dydt = N_VGetArrayPointer(ydot); - realtype propensity; + realtype propensity_ode, propensity_tau; // Extract simulation data UserData *data = static_cast(user_data); HybridSimulation *sim = data->my_sim; HybridSpecies *species = data->species_state; - HybridReaction *reactions = data->reaciton_state; - std::vector propensities = data->propensities; + HybridReaction *reactions = data->reaction_state; + std::vector &propensities = data->propensities; + // Concentrations and reactions are both used for their respective propensity evaulations. + // They both should, roughly, reflect the same data, but tau selection requires both. + std::vector &concentrations = data->concentrations; + std::vector &populations = data->populations; unsigned int num_species = sim->model->number_species; unsigned int num_reactions = sim->model->number_reactions; @@ -345,8 +361,6 @@ static int f(realtype t, N_Vector y, N_Vector ydot, void *user_data) realtype *rxn_offsets = &Y[num_species]; realtype *dydt_offsets = &dydt[num_species]; int rxn_offset_boundary = num_species + num_reactions; - std::vector concentrations(num_species); - std::vector populations(num_species); // Populate the current ODE state into the concentrations vector. // dy/dt results are initialized to zero, and become the change in propensity. @@ -374,7 +388,7 @@ static int f(realtype t, N_Vector y, N_Vector ydot, void *user_data) current_rxn = reactions[rxn_i].base_reaction; // NOTE: we may need to evaluate ODE and Tau propensities separately. // At the moment, it's unsure whether or not that's required. - propensity = sim->propensity_function->ODEEvaluate(rxn_i, concentrations); + propensity_ode = sim->propensity_function->ODEEvaluate(rxn_i, concentrations); for (spec_i = 0; spec_i < num_species; ++spec_i) { // Use the evaluated propensity to update the concentration levels and reaction state. @@ -386,15 +400,15 @@ static int f(realtype t, N_Vector y, N_Vector ydot, void *user_data) // The product on the right evaluates to 1 if species_change is positive, // and -1 if it's negative. // This is a branchless alternative to using an if-statement. - dydt[spec_i] += propensity * (-1 + 2 * (species_change > 0)); + dydt[spec_i] += propensity_ode * (-1 + 2 * (species_change > 0)); } // Process stochastic reaction state by updating the root offset for each reaction. - propensity = sim->hybrid_propensity(rxn_i, concentrations); - propensities[rxn_i] = propensity; - + propensity_tau = sim->propensity_function->TauEvaluate(rxn_i, populations); // Integrate this reaction's rxn_offset forward using the propensity. - dydt_offsets[rxn_i] += propensity; + dydt_offsets[rxn_i] += propensity_tau; + // Propensity vector is only used for tau selection, so only Tau propensity is used. + propensities[rxn_i] = propensity_tau; } return 0; From 239a8b5dd19e3928a500e571971edbde6a3abbe0 Mon Sep 17 00:00:00 2001 From: Josh C Date: Wed, 9 Jun 2021 19:24:06 -0400 Subject: [PATCH 56/88] Update Makefile debug targets - Add helper `.env` file with default directory vars - Run `make run` or `make debug`, magically works - Override `SOLVER` to use something other than hybrid - Example: `make run SOLVER=ssa` --- gillespy2/solvers/cpp/c_base/.env | 8 ++++++++ gillespy2/solvers/cpp/c_base/Makefile | 10 ++++++---- 2 files changed, 14 insertions(+), 4 deletions(-) create mode 100644 gillespy2/solvers/cpp/c_base/.env diff --git a/gillespy2/solvers/cpp/c_base/.env b/gillespy2/solvers/cpp/c_base/.env new file mode 100644 index 000000000..ccf9e0178 --- /dev/null +++ b/gillespy2/solvers/cpp/c_base/.env @@ -0,0 +1,8 @@ +mkdir -p obj +mkdir -p bin +mkdir -p dev +cp -u template/* dev +export OBJ_DIR=obj +export TEMPLATE_DIR=dev +export OUTPUT_DIR=bin +export SOLVER=hybrid diff --git a/gillespy2/solvers/cpp/c_base/Makefile b/gillespy2/solvers/cpp/c_base/Makefile index 638619475..4fc62e884 100644 --- a/gillespy2/solvers/cpp/c_base/Makefile +++ b/gillespy2/solvers/cpp/c_base/Makefile @@ -104,15 +104,17 @@ tau_leap: TauLeapingSimulation.o TauLeapingSolver.o model $(TEMPLATE_CPP) hybrid: TauHybridSimulation.o TauHybridSolver.o model sundials $(TAU_DIR)/tau.cpp $(TEMPLATE_CPP) $(CC) $(COMPILATION_ARGS) $(SUNOBJ_PATHS) $(TAU_DIR)/tau.cpp $(OBJ_DIR)/TauHybridSimulation.o $(OBJ_DIR)/TauHybridSolver.o -build: $(GPY_SOLVER); -debug: CXXFLAGS = -std=c++14 -Wall -g -debug: clean_all build; +build: $(SOLVER); run: build ./$(OUTPUT_FILE) $(EXE_ARGS) -gdb: debug + +debug: CXXFLAGS = -std=c++14 -Wall -g +debug: clean_all build; gdb --args $(OUTPUT_FILE) $(EXE_ARGS) +all: prebuild + clean: rm -rf $(OUTPUT_DIR)/*.out From bae0c2c4fbceeed48723d6700a7c148d23718628 Mon Sep 17 00:00:00 2001 From: Josh C Date: Fri, 11 Jun 2021 20:56:03 -0400 Subject: [PATCH 57/88] Organize integrator's user data separately - Add `IntegratorData` struct for storing runtime state - Separated into its own file, `integrator.cpp` - Intended to (later) be used for an abstract integrator --- gillespy2/solvers/cpp/c_base/Makefile | 4 +- .../tau_hybrid_cpp_solver/TauHybridSolver.cpp | 54 ++++--------------- .../tau_hybrid_cpp_solver/integrator.cpp | 38 +++++++++++++ .../c_base/tau_hybrid_cpp_solver/integrator.h | 24 +++++++++ 4 files changed, 75 insertions(+), 45 deletions(-) create mode 100644 gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/integrator.cpp create mode 100644 gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/integrator.h diff --git a/gillespy2/solvers/cpp/c_base/Makefile b/gillespy2/solvers/cpp/c_base/Makefile index 4fc62e884..058787572 100644 --- a/gillespy2/solvers/cpp/c_base/Makefile +++ b/gillespy2/solvers/cpp/c_base/Makefile @@ -101,8 +101,8 @@ ssa: SSASimulation.o SSASolver.o model $(TEMPLATE_CPP) tau_leap: TauLeapingSimulation.o TauLeapingSolver.o model $(TEMPLATE_CPP) $(CC) $(COMPILATION_ARGS) $(OBJ_DIR)/TauLeapingSimulation.o $(OBJ_DIR)/TauLeapingSolver.o -hybrid: TauHybridSimulation.o TauHybridSolver.o model sundials $(TAU_DIR)/tau.cpp $(TEMPLATE_CPP) - $(CC) $(COMPILATION_ARGS) $(SUNOBJ_PATHS) $(TAU_DIR)/tau.cpp $(OBJ_DIR)/TauHybridSimulation.o $(OBJ_DIR)/TauHybridSolver.o +hybrid: TauHybridSimulation.o TauHybridSolver.o model sundials $(TAU_DIR)/tau.cpp $(TAU_HYBRID_SOLVER_PATH)/integrator.cpp $(TEMPLATE_CPP) + $(CC) $(COMPILATION_ARGS) $(SUNOBJ_PATHS) $(TAU_HYBRID_SOLVER_PATH)/integrator.cpp $(TAU_DIR)/tau.cpp $(OBJ_DIR)/TauHybridSimulation.o $(OBJ_DIR)/TauHybridSolver.o build: $(SOLVER); diff --git a/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/TauHybridSolver.cpp b/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/TauHybridSolver.cpp index f6e3eea37..7b7672dab 100644 --- a/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/TauHybridSolver.cpp +++ b/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/TauHybridSolver.cpp @@ -9,6 +9,7 @@ #include "sundials_math.h" // contains the macros ABS, SUNSQR, EXP #include "TauHybridSolver.h" #include "HybridModel.h" +#include "integrator.h" // #include "statistics.h" #include "tau.h" using namespace Gillespy; @@ -17,14 +18,6 @@ using namespace Gillespy; static int f(realtype t, N_Vector y, N_Vector y_dot, void *user_data); // forward declare function to be used to solve RHS of ODE -struct UserData { - Gillespy::TauHybrid::HybridSimulation *my_sim; - Gillespy::TauHybrid::HybridSpecies *species_state; - Gillespy::TauHybrid::HybridReaction *reaction_state; - std::vector concentrations; - std::vector populations; - std::vector propensities; -}; struct IntegratorOptions{ // CVODE constants returned if bad output, or success output. // Constants: CV_SUCCESS, @@ -119,30 +112,12 @@ namespace Gillespy::TauHybrid { //TauArgs tau_args = initialize(*(simulation->model),tau_tol); double increment = simulation->timeline[1] - simulation->timeline[0]; - // Initialize the hybrid solver reaction state. - // Hybrid-specific state is localized within a set of custom structs. - HybridReaction reaction_state[num_reactions]; - HybridSpecies species_state[num_species]; - // Tau selector initialization. Used to select a valid tau step. TauArgs tau_args = initialize(model, tau_tol); - for (int rxn_i = 0; rxn_i < num_reactions; ++rxn_i) { - reaction_state[rxn_i].base_reaction = &model.reactions[rxn_i]; - } - for (int spec_i = 0; spec_i < num_species; ++spec_i) { - species_state[spec_i].base_species = &model.species[spec_i]; - } - // Population/concentration state values for each species. // TODO: change back double -> hybrid_state, once we figure out how that works std::vector current_state(num_species); - // Populations and concentrations vectors are used by the integrator. - // Kept distinct so that deterministic and stochastic propensities may be evaluated separately. - std::vector populations(num_species); - std::vector concentrations(num_species); - //initialize propensity_values to 0 for each species - std::vector propensity_values(num_reactions); // Hybrid solver is highly dependent on random numbers. // In order to do this, a URN on the range [0,1) is generated. @@ -162,25 +137,18 @@ namespace Gillespy::TauHybrid { break; } + // Struct acts as container for simulation state. + // This gets passed in to the integrator. + IntegratorData *data = new IntegratorData(simulation); + // Initialize the species population for the trajectory. for (int spec_i = 0; spec_i < num_species; ++spec_i) { - populations[spec_i] - = concentrations[spec_i] + data->populations[spec_i] + = data->concentrations[spec_i] = current_state[spec_i] = species[spec_i].initial_population; } - // Struct acts as container for simulation state. - // This gets passed in to the integrator. - UserData *data = new UserData { - simulation, - species_state, - reaction_state, - concentrations, - populations, - propensity_values - }; - // INITIALIZE INTEGRATOR STATE // Integrator is used to integrate two variable sets separately: // - concentrations for deterministic reactions @@ -238,8 +206,8 @@ namespace Gillespy::TauHybrid { tau_tol, simulation->current_time, save_time, - propensity_values, - populations + data->propensities, + data->populations ); // Determine what the next time point is. @@ -344,8 +312,8 @@ static int f(realtype t, N_Vector y, N_Vector ydot, void *user_data) realtype propensity_ode, propensity_tau; // Extract simulation data - UserData *data = static_cast(user_data); - HybridSimulation *sim = data->my_sim; + IntegratorData *data = static_cast(user_data); + HybridSimulation *sim = data->simulation; HybridSpecies *species = data->species_state; HybridReaction *reactions = data->reaction_state; std::vector &propensities = data->propensities; diff --git a/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/integrator.cpp b/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/integrator.cpp new file mode 100644 index 000000000..3f3612b80 --- /dev/null +++ b/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/integrator.cpp @@ -0,0 +1,38 @@ +#include "integrator.h" + +using namespace Gillespy::TauHybrid; + +IntegratorData::IntegratorData( + HybridSimulation *simulation, + int num_species, + int num_reactions) + : simulation(simulation), + concentrations(std::vector(num_species)), + populations(std::vector(num_species)), + propensities(std::vector(num_reactions)) +{ + species_state = new HybridSpecies[num_species]; + for (int spec_i = 0; spec_i < num_species; ++spec_i) { + species_state[spec_i].base_species = &simulation->model->species[spec_i]; + } + + reaction_state = new HybridReaction[num_reactions]; + for (int rxn_i = 0; rxn_i < num_reactions; ++rxn_i) { + reaction_state[rxn_i].base_reaction = &simulation->model->reactions[rxn_i]; + } +} + +IntegratorData::IntegratorData(HybridSimulation *simulation) + : IntegratorData( + simulation, + simulation->model->number_species, + simulation->model->number_reactions) +{ + // Empty constructor body +} + +IntegratorData::~IntegratorData() +{ + delete[] species_state; + delete[] reaction_state; +} diff --git a/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/integrator.h b/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/integrator.h new file mode 100644 index 000000000..f3b5176a8 --- /dev/null +++ b/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/integrator.h @@ -0,0 +1,24 @@ +#pragma once + +#include +#include "HybridModel.h" + +namespace Gillespy::TauHybrid +{ + + struct IntegratorData + { + HybridSimulation *simulation; + HybridSpecies *species_state; + HybridReaction *reaction_state; + + std::vector concentrations; + std::vector populations; + std::vector propensities; + + IntegratorData(HybridSimulation *simulation); + IntegratorData(HybridSimulation *simulation, int num_species, int num_reactions); + ~IntegratorData(); + }; + +} From 6be142cc0bdaa231d0a0557b195f11a432a4a23d Mon Sep 17 00:00:00 2001 From: Josh C Date: Sat, 12 Jun 2021 11:35:32 -0400 Subject: [PATCH 58/88] Initial `Integrator` class - Class to initialize, resets, and destroys Sundials data structures - Integration results stored in a simple struct for readability - Reduce code in solver source file --- gillespy2/solvers/cpp/c_base/Makefile | 4 +- .../tau_hybrid_cpp_solver/TauHybridSolver.cpp | 115 +----------------- .../tau_hybrid_cpp_solver/integrator.cpp | 47 ++++++- .../c_base/tau_hybrid_cpp_solver/integrator.h | 34 +++++- .../cpp/c_base/tau_hybrid_cpp_solver/rhs.cpp | 87 +++++++++++++ .../cpp/c_base/tau_hybrid_cpp_solver/rhs.h | 8 ++ 6 files changed, 179 insertions(+), 116 deletions(-) create mode 100644 gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/rhs.cpp create mode 100644 gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/rhs.h diff --git a/gillespy2/solvers/cpp/c_base/Makefile b/gillespy2/solvers/cpp/c_base/Makefile index 058787572..efd234b71 100644 --- a/gillespy2/solvers/cpp/c_base/Makefile +++ b/gillespy2/solvers/cpp/c_base/Makefile @@ -101,8 +101,8 @@ ssa: SSASimulation.o SSASolver.o model $(TEMPLATE_CPP) tau_leap: TauLeapingSimulation.o TauLeapingSolver.o model $(TEMPLATE_CPP) $(CC) $(COMPILATION_ARGS) $(OBJ_DIR)/TauLeapingSimulation.o $(OBJ_DIR)/TauLeapingSolver.o -hybrid: TauHybridSimulation.o TauHybridSolver.o model sundials $(TAU_DIR)/tau.cpp $(TAU_HYBRID_SOLVER_PATH)/integrator.cpp $(TEMPLATE_CPP) - $(CC) $(COMPILATION_ARGS) $(SUNOBJ_PATHS) $(TAU_HYBRID_SOLVER_PATH)/integrator.cpp $(TAU_DIR)/tau.cpp $(OBJ_DIR)/TauHybridSimulation.o $(OBJ_DIR)/TauHybridSolver.o +hybrid: TauHybridSimulation.o TauHybridSolver.o model sundials $(TAU_DIR)/tau.cpp $(TAU_HYBRID_SOLVER_PATH)/integrator.cpp $(TAU_HYBRID_SOLVER_PATH)/rhs.cpp $(TEMPLATE_CPP) + $(CC) $(COMPILATION_ARGS) $(SUNOBJ_PATHS) $(TAU_HYBRID_SOLVER_PATH)/integrator.cpp $(TAU_HYBRID_SOLVER_PATH)/rhs.cpp $(TAU_DIR)/tau.cpp $(OBJ_DIR)/TauHybridSimulation.o $(OBJ_DIR)/TauHybridSolver.o build: $(SOLVER); diff --git a/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/TauHybridSolver.cpp b/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/TauHybridSolver.cpp index 7b7672dab..d3fa7a339 100644 --- a/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/TauHybridSolver.cpp +++ b/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/TauHybridSolver.cpp @@ -177,17 +177,7 @@ namespace Gillespy::TauHybrid { } // Build the ODE memory object and initialize it. - // Accepts initial integrator state y0, start time t0, and RHS f. - void *cvode_mem = CVodeCreate(CV_BDF); - realtype t0 = 0; - int flag = 0; - flag = CVodeInit(cvode_mem, f, t0, y0); - flag = CVodeSStolerances(cvode_mem, GPY_HYBRID_RELTOL, GPY_HYBRID_ABSTOL); - - // Build the Linear Solver object and initialize it. - SUNLinearSolver LS = SUNLinSol_SPGMR(y0, 0, 0); - flag = CVodeSetUserData(cvode_mem, data); - flag = CVodeSetLinearSolver(cvode_mem, LS, NULL); + Integrator sol(simulation, y0, GPY_HYBRID_RELTOL, GPY_HYBRID_ABSTOL); // SIMULATION STEP LOOP double next_time; @@ -218,14 +208,8 @@ namespace Gillespy::TauHybrid { // Integration Step // For deterministic reactions, the concentrations are updated directly. // For stochastic reactions, integration updates the rxn_offsets vector. - flag = CVode(cvode_mem, next_time, y0, &next_time, CV_NORMAL); - - // "Extract" the different partitions of the vector. - // [ --- concentrations --- | --- rxn_offsets --- ] - // concentrations: bounded by [0, num_species) - // rxn_offsets: bounded by [num_species, num_species + num_reactions) - realtype *concentrations = NV_DATA_S(y0); - realtype *rxn_offsets = NV_DATA_S(y0) + num_species; + // flag = CVode(cvode_mem, next_time, y0, &next_time, CV_NORMAL); + IntegrationResults result = sol.integrate(next_time); // The newly-updated reaction_states vector may need to be reconciled now. // A positive reaction_state means reactions have potentially fired. @@ -233,7 +217,7 @@ namespace Gillespy::TauHybrid { for (int rxn_i = 0; rxn_i < num_reactions; ++rxn_i) { // Temporary variable for the reaction's state. // Does not get updated unless the changes are deemed valid. - double rxn_state = rxn_offsets[rxn_i]; + double rxn_state = result.reactions[rxn_i]; // 0-initialize our population_changes array. for (int p_i = 0; p_i < num_species; ++p_i) @@ -270,7 +254,7 @@ namespace Gillespy::TauHybrid { for (int p_i = 0; p_i < num_species; ++p_i) { current_state[p_i] += population_changes[p_i]; } - rxn_offsets[rxn_i] = rxn_state; + result.reactions[rxn_i] = rxn_state; } // Output the results for this time step. @@ -279,7 +263,7 @@ namespace Gillespy::TauHybrid { while (save_time <= next_time) { // Write each species, one at a time (from ODE solution) for (int spec_i = 0; spec_i < num_species; ++spec_i) { - // current_state[spec_i] += NV_Ith_S(y0, spec_i); + // current_state[spec_i] += result.concentrations[spec_i]; simulation->trajectories_hybrid[traj][save_time][spec_i].discrete = current_state[spec_i]; } save_time += increment; @@ -288,96 +272,9 @@ namespace Gillespy::TauHybrid { } // End of trajectory - // Clean up integrator data structures - N_VDestroy_Serial(y0); - CVodeFree(&cvode_mem); - SUNLinSolFree_SPGMR(LS); delete data; delete[] population_changes; } } } } - -/** - * Integrator function for ODE linear solver. - * This gets passed directly to the Sundials ODE solver once initialized. - */ -static int f(realtype t, N_Vector y, N_Vector ydot, void *user_data) -{ - using namespace Gillespy::TauHybrid; - // Get y(t) vector and f(t, y) vector - realtype *Y = N_VGetArrayPointer(y); - realtype *dydt = N_VGetArrayPointer(ydot); - realtype propensity_ode, propensity_tau; - - // Extract simulation data - IntegratorData *data = static_cast(user_data); - HybridSimulation *sim = data->simulation; - HybridSpecies *species = data->species_state; - HybridReaction *reactions = data->reaction_state; - std::vector &propensities = data->propensities; - // Concentrations and reactions are both used for their respective propensity evaulations. - // They both should, roughly, reflect the same data, but tau selection requires both. - std::vector &concentrations = data->concentrations; - std::vector &populations = data->populations; - unsigned int num_species = sim->model->number_species; - unsigned int num_reactions = sim->model->number_reactions; - - // Differentiate different regions of the input/output vectors. - // First half is for concentrations, second half is for reaction offsets. - realtype *rxn_offsets = &Y[num_species]; - realtype *dydt_offsets = &dydt[num_species]; - int rxn_offset_boundary = num_species + num_reactions; - - // Populate the current ODE state into the concentrations vector. - // dy/dt results are initialized to zero, and become the change in propensity. - unsigned int spec_i; - for (spec_i = 0; spec_i < num_species; ++spec_i) { - populations[spec_i] = concentrations[spec_i] = Y[spec_i]; - dydt[spec_i] = 0; - } - - // Populate the current stochastic state into the root offset vector. - // dy/dt results are initialized to zero, and become the change in offset. - unsigned int rxn_i; - for (rxn_i = 0; rxn_i < num_reactions; ++rxn_i) { - dydt_offsets[rxn_i] = 0; - } - - // Each species has a "spot" in the y and f(y,t) vector. - // For each species, place the result of f(y,t) into dydt vector. - int species_change; - Reaction *current_rxn; - - // Process deterministic propensity state - // These updates get written directly to the integrator's concentration state - for (rxn_i = 0; rxn_i < num_reactions; ++rxn_i) { - current_rxn = reactions[rxn_i].base_reaction; - // NOTE: we may need to evaluate ODE and Tau propensities separately. - // At the moment, it's unsure whether or not that's required. - propensity_ode = sim->propensity_function->ODEEvaluate(rxn_i, concentrations); - - for (spec_i = 0; spec_i < num_species; ++spec_i) { - // Use the evaluated propensity to update the concentration levels and reaction state. - // Propensity is treated as positive if it's a product, negative if it's a reactant. - species_change = current_rxn->species_change[spec_i]; - if (species_change == 0) - continue; - - // The product on the right evaluates to 1 if species_change is positive, - // and -1 if it's negative. - // This is a branchless alternative to using an if-statement. - dydt[spec_i] += propensity_ode * (-1 + 2 * (species_change > 0)); - } - - // Process stochastic reaction state by updating the root offset for each reaction. - propensity_tau = sim->propensity_function->TauEvaluate(rxn_i, populations); - // Integrate this reaction's rxn_offset forward using the propensity. - dydt_offsets[rxn_i] += propensity_tau; - // Propensity vector is only used for tau selection, so only Tau propensity is used. - propensities[rxn_i] = propensity_tau; - } - - return 0; -}; diff --git a/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/integrator.cpp b/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/integrator.cpp index 3f3612b80..ebf989dfb 100644 --- a/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/integrator.cpp +++ b/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/integrator.cpp @@ -1,4 +1,7 @@ #include "integrator.h" +#include "rhs.h" + +static bool validate(int retcode); using namespace Gillespy::TauHybrid; @@ -26,13 +29,49 @@ IntegratorData::IntegratorData(HybridSimulation *simulation) : IntegratorData( simulation, simulation->model->number_species, - simulation->model->number_reactions) -{ - // Empty constructor body -} + simulation->model->number_reactions) {} IntegratorData::~IntegratorData() { delete[] species_state; delete[] reaction_state; } + +Integrator::Integrator(HybridSimulation *simulation, N_Vector y0, double reltol, double abstol) + : y(y0), + data(simulation), + num_reactions(simulation->model->number_reactions), + num_species(simulation->model->number_species) +{ + cvode_mem = CVodeCreate(CV_BDF); + validate(CVodeInit(cvode_mem, rhs, t, y)); + validate(CVodeSStolerances(cvode_mem, reltol, abstol)); + + solver = SUNLinSol_SPGMR(y, 0, 0); + validate(CVodeSetUserData(cvode_mem, &data)); + validate(CVodeSetLinearSolver(cvode_mem, solver, NULL)); +} + +Integrator::~Integrator() +{ + N_VDestroy_Serial(y); + CVodeFree(&cvode_mem); + SUNLinSolFree_SPGMR(solver); +} + +IntegrationResults Integrator::integrate(double &t) +{ + if (!validate(CVode(cvode_mem, t, y, &t, CV_NORMAL))) { + return { nullptr, nullptr }; + } + + return { + NV_DATA_S(y), // NV_DATA_S instead? + NV_DATA_S(y) + num_species + }; +} + +bool validate(int retcode) +{ + return true; +} diff --git a/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/integrator.h b/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/integrator.h index f3b5176a8..90b693128 100644 --- a/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/integrator.h +++ b/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/integrator.h @@ -1,7 +1,11 @@ #pragma once -#include #include "HybridModel.h" +#include "cvode.h" +#include "sunlinsol_spgmr.h" +#include "sundials_types.h" +#include "nvector_serial.h" +#include namespace Gillespy::TauHybrid { @@ -18,7 +22,35 @@ namespace Gillespy::TauHybrid IntegratorData(HybridSimulation *simulation); IntegratorData(HybridSimulation *simulation, int num_species, int num_reactions); + IntegratorData(IntegratorData &prev_data); ~IntegratorData(); }; + // [ --- concentrations --- | --- rxn_offsets --- ] + struct IntegrationResults + { + // concentrations: bounded by [0, num_species) + realtype *concentrations; + // reactions: bounded by [num_species, num_species + num_reactions) + realtype *reactions; + }; + + class Integrator + { + private: + void *cvode_mem; + SUNLinearSolver solver; + int num_species; + int num_reactions; + public: + N_Vector y; + realtype t; + IntegrationResults integrate(double &t); + IntegratorData data; + + Integrator(HybridSimulation *simulation); + Integrator(HybridSimulation *simulation, N_Vector y0, double reltol, double abstol); + ~Integrator(); + }; + } diff --git a/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/rhs.cpp b/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/rhs.cpp new file mode 100644 index 000000000..21f058f06 --- /dev/null +++ b/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/rhs.cpp @@ -0,0 +1,87 @@ +#include "rhs.h" +#include "HybridModel.h" +#include "integrator.h" + +using namespace Gillespy::TauHybrid; + +/** + * Integrator function for ODE linear solver. + * This gets passed directly to the Sundials ODE solver once initialized. + */ +int Gillespy::TauHybrid::rhs(realtype t, N_Vector y, N_Vector ydot, void *user_data) +{ + // Get y(t) vector and f(t, y) vector + realtype *Y = N_VGetArrayPointer(y); + realtype *dydt = N_VGetArrayPointer(ydot); + realtype propensity_ode, propensity_tau; + + // Extract simulation data + IntegratorData *data = static_cast(user_data); + HybridSimulation *sim = data->simulation; + HybridSpecies *species = data->species_state; + HybridReaction *reactions = data->reaction_state; + std::vector &propensities = data->propensities; + // Concentrations and reactions are both used for their respective propensity evaulations. + // They both should, roughly, reflect the same data, but tau selection requires both. + std::vector &concentrations = data->concentrations; + std::vector &populations = data->populations; + unsigned int num_species = sim->model->number_species; + unsigned int num_reactions = sim->model->number_reactions; + + // Differentiate different regions of the input/output vectors. + // First half is for concentrations, second half is for reaction offsets. + realtype *rxn_offsets = &Y[num_species]; + realtype *dydt_offsets = &dydt[num_species]; + int rxn_offset_boundary = num_species + num_reactions; + + // Populate the current ODE state into the concentrations vector. + // dy/dt results are initialized to zero, and become the change in propensity. + unsigned int spec_i; + for (spec_i = 0; spec_i < num_species; ++spec_i) { + populations[spec_i] = concentrations[spec_i] = Y[spec_i]; + dydt[spec_i] = 0; + } + + // Populate the current stochastic state into the root offset vector. + // dy/dt results are initialized to zero, and become the change in offset. + unsigned int rxn_i; + for (rxn_i = 0; rxn_i < num_reactions; ++rxn_i) { + dydt_offsets[rxn_i] = 0; + } + + // Each species has a "spot" in the y and f(y,t) vector. + // For each species, place the result of f(y,t) into dydt vector. + int species_change; + Gillespy::Reaction *current_rxn; + + // Process deterministic propensity state + // These updates get written directly to the integrator's concentration state + for (rxn_i = 0; rxn_i < num_reactions; ++rxn_i) { + current_rxn = reactions[rxn_i].base_reaction; + // NOTE: we may need to evaluate ODE and Tau propensities separately. + // At the moment, it's unsure whether or not that's required. + propensity_ode = sim->propensity_function->ODEEvaluate(rxn_i, concentrations); + + for (spec_i = 0; spec_i < num_species; ++spec_i) { + // Use the evaluated propensity to update the concentration levels and reaction state. + // Propensity is treated as positive if it's a product, negative if it's a reactant. + species_change = current_rxn->species_change[spec_i]; + if (species_change == 0) + continue; + + // The product on the right evaluates to 1 if species_change is positive, + // and -1 if it's negative. + // This is a branchless alternative to using an if-statement. + dydt[spec_i] += propensity_ode * (-1 + 2 * (species_change > 0)); + } + + // Process stochastic reaction state by updating the root offset for each reaction. + propensity_tau = sim->propensity_function->TauEvaluate(rxn_i, populations); + // Integrate this reaction's rxn_offset forward using the propensity. + dydt_offsets[rxn_i] += propensity_tau; + // Propensity vector is only used for tau selection, so only Tau propensity is used. + propensities[rxn_i] = propensity_tau; + } + + return 0; +}; diff --git a/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/rhs.h b/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/rhs.h new file mode 100644 index 000000000..b0b106bd1 --- /dev/null +++ b/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/rhs.h @@ -0,0 +1,8 @@ +#pragma once + +#include "cvode.h" + +namespace Gillespy::TauHybrid +{ + int rhs(realtype t, N_Vector y, N_Vector ydot, void *user_data); +} From e4871eb650e355f50fa37920e307a2710dbb601a Mon Sep 17 00:00:00 2001 From: Josh C Date: Mon, 14 Jun 2021 10:51:40 -0400 Subject: [PATCH 59/88] Move N_Vector initialization into `integrator.cpp` - New function: `init_model_vector` to create N_Vector - Move concentration/population init (for vector) into data constructor - Switch `if (simulation)` with flight check fail for readability --- .../tau_hybrid_cpp_solver/TauHybridSolver.cpp | 289 ++++++++---------- .../tau_hybrid_cpp_solver/integrator.cpp | 58 ++++ .../c_base/tau_hybrid_cpp_solver/integrator.h | 14 + 3 files changed, 203 insertions(+), 158 deletions(-) diff --git a/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/TauHybridSolver.cpp b/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/TauHybridSolver.cpp index d3fa7a339..28c507a14 100644 --- a/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/TauHybridSolver.cpp +++ b/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/TauHybridSolver.cpp @@ -103,178 +103,151 @@ namespace Gillespy::TauHybrid { { //timeouts not supported right now?? // signal(SIGINT, signalHandler); - if (simulation) { - Model &model = *(simulation->model); - int num_species = model.number_species; - int num_reactions = model.number_reactions; - int num_trajectories = simulation->number_trajectories; - std::unique_ptr &species = model.species; - //TauArgs tau_args = initialize(*(simulation->model),tau_tol); - double increment = simulation->timeline[1] - simulation->timeline[0]; - - // Tau selector initialization. Used to select a valid tau step. - TauArgs tau_args = initialize(model, tau_tol); - - // Population/concentration state values for each species. - // TODO: change back double -> hybrid_state, once we figure out how that works - std::vector current_state(num_species); - - // Hybrid solver is highly dependent on random numbers. - // In order to do this, a URN on the range [0,1) is generated. - // log( uniform(rng) ) returns a real number on the range (-inf, 0). - // TODO: either assign a seed or set seed to be configurable - std::mt19937_64 rng; - std::uniform_real_distribution uniform(0, 1); - - //copy initial state for each trajectory - for(int s = 0; s < num_species; s++){ - simulation->trajectories_hybrid[0][0][s].continuous = species[s].initial_population; - current_state[s] = species[s].initial_population; - } - //Simulate for each trajectory - for(int traj = 0; traj < num_trajectories; traj++){ - if (interrupted){ - break; - } - - // Struct acts as container for simulation state. - // This gets passed in to the integrator. - IntegratorData *data = new IntegratorData(simulation); + if (simulation == NULL) { + return; + } - // Initialize the species population for the trajectory. - for (int spec_i = 0; spec_i < num_species; ++spec_i) { - data->populations[spec_i] - = data->concentrations[spec_i] - = current_state[spec_i] - = species[spec_i].initial_population; - } + Model &model = *(simulation->model); + int num_species = model.number_species; + int num_reactions = model.number_reactions; + int num_trajectories = simulation->number_trajectories; + std::unique_ptr &species = model.species; + //TauArgs tau_args = initialize(*(simulation->model),tau_tol); + double increment = simulation->timeline[1] - simulation->timeline[0]; + + // Tau selector initialization. Used to select a valid tau step. + TauArgs tau_args = initialize(model, tau_tol); + + // Population/concentration state values for each species. + // TODO: change back double -> hybrid_state, once we figure out how that works + std::vector current_state(num_species); + + // Hybrid solver is highly dependent on random numbers. + // In order to do this, a URN on the range [0,1) is generated. + // log( uniform(rng) ) returns a real number on the range (-inf, 0). + // TODO: either assign a seed or set seed to be configurable + std::mt19937_64 rng; + std::uniform_real_distribution uniform(0, 1); + + + //copy initial state for each trajectory + for(int s = 0; s < num_species; s++){ + simulation->trajectories_hybrid[0][0][s].continuous = species[s].initial_population; + current_state[s] = species[s].initial_population; + } + //Simulate for each trajectory + for(int traj = 0; traj < num_trajectories; traj++){ + if (interrupted){ + break; + } - // INITIALIZE INTEGRATOR STATE - // Integrator is used to integrate two variable sets separately: - // - concentrations for deterministic reactions - // - reaction offsets for stochastic reactions - // [ --- concentrations --- | --- rxn_offsets --- ] - // concentrations: bounded by [0, num_species) - // rxn_offsets: bounded by [num_species, num_species + num_reactions) - int rxn_offset_boundary = num_species + num_reactions; - - // The first half of the integration vector is used for integrating species concentrations. - // [ --- concentrations --- | ... - N_Vector y0 = N_VNew_Serial(rxn_offset_boundary); - for (int spec_i = 0; spec_i < num_species; ++spec_i) { - NV_Ith_S(y0, spec_i) = species[spec_i].initial_population; - } + // Struct acts as container for simulation state. + // This gets passed in to the integrator. + IntegratorData *data = new IntegratorData(simulation); + URNGenerator urn; + N_Vector y0 = init_model_vector(model, urn); + Integrator sol(simulation, y0, GPY_HYBRID_RELTOL, GPY_HYBRID_ABSTOL); - // The second half represents the current "randomized state" for each reaction. - // ... | --- rxn_offsets --- ] - for (int rxn_i = num_species; rxn_i < rxn_offset_boundary; ++rxn_i) { - // Represents the current "randomized state" for each reaction, used as a - // helper value to determine if/how many stochastic reactions fire. - // This gets initialized to a random negative offset, and gets "less negative" - // during the integration step. - // After each integration step, the reaction_state is used to count stochastic reactions. - NV_Ith_S(y0, rxn_i) = log(uniform(rng)); - } + // Initialize the species population for the trajectory. + for (int spec_i = 0; spec_i < num_species; ++spec_i) { + current_state[spec_i] = species[spec_i].initial_population; + } - // Build the ODE memory object and initialize it. - Integrator sol(simulation, y0, GPY_HYBRID_RELTOL, GPY_HYBRID_ABSTOL); - - // SIMULATION STEP LOOP - double next_time; - double tau_step = 0.0; - int save_time = 0; - - // Temporary array to store changes to dependent species. - // Should be 0-initialized each time it's used. - int *population_changes = new int[num_species]; - simulation->current_time = 0; - while (simulation->current_time < simulation->end_time) { - // Expected tau step is determined. - tau_step = select( - model, - tau_args, - tau_tol, - simulation->current_time, - save_time, - data->propensities, - data->populations - ); - - // Determine what the next time point is. - // This will become current_time on the next iteration. - // If a retry with a smaller tau_step is deemed necessary, this will change. - next_time = simulation->current_time + tau_step; - - // Integration Step - // For deterministic reactions, the concentrations are updated directly. - // For stochastic reactions, integration updates the rxn_offsets vector. - // flag = CVode(cvode_mem, next_time, y0, &next_time, CV_NORMAL); - IntegrationResults result = sol.integrate(next_time); - - // The newly-updated reaction_states vector may need to be reconciled now. - // A positive reaction_state means reactions have potentially fired. - // NOTE: it is possible for a population to swing negative, where a smaller Tau is needed. - for (int rxn_i = 0; rxn_i < num_reactions; ++rxn_i) { - // Temporary variable for the reaction's state. - // Does not get updated unless the changes are deemed valid. - double rxn_state = result.reactions[rxn_i]; - - // 0-initialize our population_changes array. - for (int p_i = 0; p_i < num_species; ++p_i) - population_changes[p_i] = 0; - - // Use the current reaction_state to count the number of firings. - // If a negative population is detected, then the loop breaks prematurely. - bool invalid_state = false; - while (rxn_state >= 0 && !invalid_state) { - // "Fire" a reaction by recording changes in dependent species. - // If a negative value is detected, break without saving changes. - for (int spec_i = 0; spec_i < num_species; ++spec_i) { - population_changes[spec_i] += - model.reactions[rxn_i].species_change[spec_i]; - if (current_state[spec_i] + population_changes[spec_i] < 0) { - invalid_state = true; - } + // INITIALIZE INTEGRATOR STATE + int rxn_offset_boundary = num_species + num_reactions; + + // SIMULATION STEP LOOP + double next_time; + double tau_step = 0.0; + int save_time = 0; + + // Temporary array to store changes to dependent species. + // Should be 0-initialized each time it's used. + int *population_changes = new int[num_species]; + simulation->current_time = 0; + while (simulation->current_time < simulation->end_time) { + // Expected tau step is determined. + tau_step = select( + model, + tau_args, + tau_tol, + simulation->current_time, + save_time, + data->propensities, + data->populations + ); + + // Determine what the next time point is. + // This will become current_time on the next iteration. + // If a retry with a smaller tau_step is deemed necessary, this will change. + next_time = simulation->current_time + tau_step; + + // Integration Step + // For deterministic reactions, the concentrations are updated directly. + // For stochastic reactions, integration updates the rxn_offsets vector. + // flag = CVode(cvode_mem, next_time, y0, &next_time, CV_NORMAL); + IntegrationResults result = sol.integrate(next_time); + + // The newly-updated reaction_states vector may need to be reconciled now. + // A positive reaction_state means reactions have potentially fired. + // NOTE: it is possible for a population to swing negative, where a smaller Tau is needed. + for (int rxn_i = 0; rxn_i < num_reactions; ++rxn_i) { + // Temporary variable for the reaction's state. + // Does not get updated unless the changes are deemed valid. + double rxn_state = result.reactions[rxn_i]; + + // 0-initialize our population_changes array. + for (int p_i = 0; p_i < num_species; ++p_i) + population_changes[p_i] = 0; + + // Use the current reaction_state to count the number of firings. + // If a negative population is detected, then the loop breaks prematurely. + bool invalid_state = false; + while (rxn_state >= 0 && !invalid_state) { + // "Fire" a reaction by recording changes in dependent species. + // If a negative value is detected, break without saving changes. + for (int spec_i = 0; spec_i < num_species; ++spec_i) { + population_changes[spec_i] += + model.reactions[rxn_i].species_change[spec_i]; + if (current_state[spec_i] + population_changes[spec_i] < 0) { + invalid_state = true; } - - // uniform(rng) is a random number on range (0,1), always fractional - // This means that log(uniform(rng)) is always negative - rxn_state += log(uniform(rng)); } - // Positive reaction state means a negative population was detected. - // Only update state with the given population changes if valid. - if (invalid_state) { - // TODO: invalidate reaction timestep, reset integrator state, try again - std::cerr << "Integration step failed; RIP" << std::endl; - continue; - } + rxn_state += log(urn.next()); + } - // "Permanently" update the rxn_state and populations. - for (int p_i = 0; p_i < num_species; ++p_i) { - current_state[p_i] += population_changes[p_i]; - } - result.reactions[rxn_i] = rxn_state; + // Positive reaction state means a negative population was detected. + // Only update state with the given population changes if valid. + if (invalid_state) { + // TODO: invalidate reaction timestep, reset integrator state, try again + std::cerr << "Integration step failed; RIP" << std::endl; + // continue; } - // Output the results for this time step. - simulation->current_time = next_time; - - while (save_time <= next_time) { - // Write each species, one at a time (from ODE solution) - for (int spec_i = 0; spec_i < num_species; ++spec_i) { - // current_state[spec_i] += result.concentrations[spec_i]; - simulation->trajectories_hybrid[traj][save_time][spec_i].discrete = current_state[spec_i]; - } - save_time += increment; + // "Permanently" update the rxn_state and populations. + for (int p_i = 0; p_i < num_species; ++p_i) { + current_state[p_i] += population_changes[p_i]; } - + result.reactions[rxn_i] = rxn_state; } - // End of trajectory - delete data; - delete[] population_changes; + // Output the results for this time step. + simulation->current_time = next_time; + + while (save_time <= next_time) { + // Write each species, one at a time (from ODE solution) + for (int spec_i = 0; spec_i < num_species; ++spec_i) { + // current_state[spec_i] += result.concentrations[spec_i]; + simulation->trajectories_hybrid[traj][save_time][spec_i].discrete = current_state[spec_i]; + } + save_time += increment; + } } + + // End of trajectory + delete data; + delete[] population_changes; } } } diff --git a/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/integrator.cpp b/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/integrator.cpp index ebf989dfb..81750a9d2 100644 --- a/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/integrator.cpp +++ b/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/integrator.cpp @@ -43,6 +43,12 @@ Integrator::Integrator(HybridSimulation *simulation, N_Vector y0, double reltol, num_reactions(simulation->model->number_reactions), num_species(simulation->model->number_species) { + for (int spec_i = 0; spec_i < num_species; ++spec_i) { + data.populations[spec_i] + = data.concentrations[spec_i] + = simulation->model->species[spec_i].initial_population; + } + cvode_mem = CVodeCreate(CV_BDF); validate(CVodeInit(cvode_mem, rhs, t, y)); validate(CVodeSStolerances(cvode_mem, reltol, abstol)); @@ -71,6 +77,58 @@ IntegrationResults Integrator::integrate(double &t) }; } +URNGenerator::URNGenerator() + : uniform(0, 1) {} + +URNGenerator::URNGenerator(double seed) + : uniform(0, 1), + rng(seed) {} + +/* Generate a new random floating-point number on the range [0,1). + * Uses a uniform distribution to generate. + */ +double URNGenerator::next() +{ + return uniform(rng); +} + +/* Initialize a SUNDials N_Vector based on information provided in the model. + * + */ +N_Vector Gillespy::TauHybrid::init_model_vector(Gillespy::Model &model, URNGenerator urn) +{ + int rxn_offset_boundary = model.number_reactions + model.number_species; + + // INITIAL INTEGRATOR STATE VECTOR + // Integrator is used to integrate two vector regions separately: + // - concentrations for deterministic reactions + // - reaction offsets for stochastic reactions + // [ --- concentrations --- | --- rxn_offsets --- ] + // concentrations: bounded by [0, num_species) + // rxn_offsets: bounded by [num_species, num_species + num_reactions) + N_Vector y0 = N_VNew_Serial(rxn_offset_boundary); + + // The first half of the integration vector is used for integrating species concentrations. + // [ --- concentrations --- | ... + for (int spec_i = 0; spec_i < model.number_species; ++spec_i) { + NV_Ith_S(y0, spec_i) = model.species[spec_i].initial_population; + } + + // The second half represents the current "randomized state" for each reaction. + // ... | --- rxn_offsets --- ] + for (int rxn_i = model.number_species; rxn_i < rxn_offset_boundary; ++rxn_i) { + // Represents the current "randomized state" for each reaction, used as a + // helper value to determine if/how many stochastic reactions fire. + // This gets initialized to a random negative offset, and gets "less negative" + // during the integration step. + // After each integration step, the reaction_state is used to count stochastic reactions. + NV_Ith_S(y0, rxn_i) = log(urn.next()); + } + + return y0; +} + + bool validate(int retcode) { return true; diff --git a/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/integrator.h b/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/integrator.h index 90b693128..57a491120 100644 --- a/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/integrator.h +++ b/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/integrator.h @@ -6,6 +6,7 @@ #include "sundials_types.h" #include "nvector_serial.h" #include +#include namespace Gillespy::TauHybrid { @@ -53,4 +54,17 @@ namespace Gillespy::TauHybrid ~Integrator(); }; + struct URNGenerator + { + private: + std::uniform_real_distribution uniform; + std::mt19937_64 rng; + public: + double next(); + URNGenerator(); + URNGenerator(double seed); + }; + + N_Vector init_model_vector(Model &model, URNGenerator urn); + } From fcbf474591ca05ab995c36071ccac1333d0460a2 Mon Sep 17 00:00:00 2001 From: Josh C Date: Tue, 15 Jun 2021 10:02:56 -0400 Subject: [PATCH 60/88] Partition check in integrator RHS - Switch statements in RHS and post-integration to check if discrete or continuous - Reactions treated differently based on reaction type - Modify `save_step` so `Dky` doesn't get a timestep of 0 --- .../tau_hybrid_cpp_solver/TauHybridSolver.cpp | 91 +++++++++---------- .../tau_hybrid_cpp_solver/integrator.cpp | 9 +- .../c_base/tau_hybrid_cpp_solver/integrator.h | 11 ++- .../cpp/c_base/tau_hybrid_cpp_solver/rhs.cpp | 35 ++++--- 4 files changed, 81 insertions(+), 65 deletions(-) diff --git a/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/TauHybridSolver.cpp b/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/TauHybridSolver.cpp index 28c507a14..7dd7c5845 100644 --- a/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/TauHybridSolver.cpp +++ b/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/TauHybridSolver.cpp @@ -14,10 +14,6 @@ #include "tau.h" using namespace Gillespy; -// #define NV_Ith_S(v,i) (NV_DATA_S(v)[i]) // Access to individual components of data array, of N len vector - -static int f(realtype t, N_Vector y, N_Vector y_dot, void *user_data); // forward declare function to be used to solve RHS of ODE - struct IntegratorOptions{ // CVODE constants returned if bad output, or success output. // Constants: CV_SUCCESS, @@ -120,15 +116,7 @@ namespace Gillespy::TauHybrid { // Population/concentration state values for each species. // TODO: change back double -> hybrid_state, once we figure out how that works - std::vector current_state(num_species); - - // Hybrid solver is highly dependent on random numbers. - // In order to do this, a URN on the range [0,1) is generated. - // log( uniform(rng) ) returns a real number on the range (-inf, 0). - // TODO: either assign a seed or set seed to be configurable - std::mt19937_64 rng; - std::uniform_real_distribution uniform(0, 1); - + std::vector current_state(num_species); //copy initial state for each trajectory for(int s = 0; s < num_species; s++){ @@ -141,9 +129,6 @@ namespace Gillespy::TauHybrid { break; } - // Struct acts as container for simulation state. - // This gets passed in to the integrator. - IntegratorData *data = new IntegratorData(simulation); URNGenerator urn; N_Vector y0 = init_model_vector(model, urn); Integrator sol(simulation, y0, GPY_HYBRID_RELTOL, GPY_HYBRID_ABSTOL); @@ -159,7 +144,7 @@ namespace Gillespy::TauHybrid { // SIMULATION STEP LOOP double next_time; double tau_step = 0.0; - int save_time = 0; + int save_time = simulation->timeline[1]; // Temporary array to store changes to dependent species. // Should be 0-initialized each time it's used. @@ -173,8 +158,8 @@ namespace Gillespy::TauHybrid { tau_tol, simulation->current_time, save_time, - data->propensities, - data->populations + sol.data.propensities, + sol.data.populations ); // Determine what the next time point is. @@ -186,7 +171,7 @@ namespace Gillespy::TauHybrid { // For deterministic reactions, the concentrations are updated directly. // For stochastic reactions, integration updates the rxn_offsets vector. // flag = CVode(cvode_mem, next_time, y0, &next_time, CV_NORMAL); - IntegrationResults result = sol.integrate(next_time); + IntegrationResults result = sol.integrate(&next_time); // The newly-updated reaction_states vector may need to be reconciled now. // A positive reaction_state means reactions have potentially fired. @@ -196,40 +181,52 @@ namespace Gillespy::TauHybrid { // Does not get updated unless the changes are deemed valid. double rxn_state = result.reactions[rxn_i]; - // 0-initialize our population_changes array. - for (int p_i = 0; p_i < num_species; ++p_i) - population_changes[p_i] = 0; - // Use the current reaction_state to count the number of firings. // If a negative population is detected, then the loop breaks prematurely. bool invalid_state = false; - while (rxn_state >= 0 && !invalid_state) { - // "Fire" a reaction by recording changes in dependent species. - // If a negative value is detected, break without saving changes. - for (int spec_i = 0; spec_i < num_species; ++spec_i) { - population_changes[spec_i] += - model.reactions[rxn_i].species_change[spec_i]; - if (current_state[spec_i] + population_changes[spec_i] < 0) { - invalid_state = true; + switch (sol.data.reaction_state[rxn_i].mode) { + case SimulationState::DISCRETE: + + // 0-initialize our population_changes array. + for (int p_i = 0; p_i < num_species; ++p_i) + population_changes[p_i] = 0; + + while (rxn_state >= 0 && !invalid_state) { + // "Fire" a reaction by recording changes in dependent species. + // If a negative value is detected, break without saving changes. + for (int spec_i = 0; spec_i < num_species; ++spec_i) { + population_changes[spec_i] += + model.reactions[rxn_i].species_change[spec_i]; + if (current_state[spec_i] + population_changes[spec_i] < 0) { + invalid_state = true; + } } + + rxn_state += log(urn.next()); } - rxn_state += log(urn.next()); - } + // Positive reaction state means a negative population was detected. + // Only update state with the given population changes if valid. + if (invalid_state) { + // TODO: invalidate reaction timestep, reset integrator state, try again + std::cerr << "Integration step failed; RIP" << std::endl; + continue; + } - // Positive reaction state means a negative population was detected. - // Only update state with the given population changes if valid. - if (invalid_state) { - // TODO: invalidate reaction timestep, reset integrator state, try again - std::cerr << "Integration step failed; RIP" << std::endl; - // continue; - } + // "Permanently" update the rxn_state and populations. + for (int p_i = 0; p_i < num_species; ++p_i) { + current_state[p_i] += population_changes[p_i]; + } + result.reactions[rxn_i] = rxn_state; + break; - // "Permanently" update the rxn_state and populations. - for (int p_i = 0; p_i < num_species; ++p_i) { - current_state[p_i] += population_changes[p_i]; + case SimulationState::CONTINUOUS: + default: + for (int spec_i = 0; spec_i < num_species; ++spec_i) { + current_state[spec_i] += result.concentrations[spec_i]; + } + break; } - result.reactions[rxn_i] = rxn_state; } // Output the results for this time step. @@ -238,15 +235,13 @@ namespace Gillespy::TauHybrid { while (save_time <= next_time) { // Write each species, one at a time (from ODE solution) for (int spec_i = 0; spec_i < num_species; ++spec_i) { - // current_state[spec_i] += result.concentrations[spec_i]; - simulation->trajectories_hybrid[traj][save_time][spec_i].discrete = current_state[spec_i]; + simulation->trajectories_hybrid[traj][save_time][spec_i].continuous = current_state[spec_i]; } save_time += increment; } } // End of trajectory - delete data; delete[] population_changes; } } diff --git a/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/integrator.cpp b/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/integrator.cpp index 81750a9d2..9bc64de45 100644 --- a/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/integrator.cpp +++ b/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/integrator.cpp @@ -5,6 +5,7 @@ static bool validate(int retcode); using namespace Gillespy::TauHybrid; + IntegratorData::IntegratorData( HybridSimulation *simulation, int num_species, @@ -37,6 +38,7 @@ IntegratorData::~IntegratorData() delete[] reaction_state; } + Integrator::Integrator(HybridSimulation *simulation, N_Vector y0, double reltol, double abstol) : y(y0), data(simulation), @@ -65,9 +67,9 @@ Integrator::~Integrator() SUNLinSolFree_SPGMR(solver); } -IntegrationResults Integrator::integrate(double &t) +IntegrationResults Integrator::integrate(double *t) { - if (!validate(CVode(cvode_mem, t, y, &t, CV_NORMAL))) { + if (!validate(CVode(cvode_mem, *t, y, t, CV_NORMAL))) { return { nullptr, nullptr }; } @@ -77,6 +79,7 @@ IntegrationResults Integrator::integrate(double &t) }; } + URNGenerator::URNGenerator() : uniform(0, 1) {} @@ -84,6 +87,7 @@ URNGenerator::URNGenerator(double seed) : uniform(0, 1), rng(seed) {} + /* Generate a new random floating-point number on the range [0,1). * Uses a uniform distribution to generate. */ @@ -92,6 +96,7 @@ double URNGenerator::next() return uniform(rng); } + /* Initialize a SUNDials N_Vector based on information provided in the model. * */ diff --git a/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/integrator.h b/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/integrator.h index 57a491120..d71445256 100644 --- a/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/integrator.h +++ b/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/integrator.h @@ -27,7 +27,14 @@ namespace Gillespy::TauHybrid ~IntegratorData(); }; - // [ --- concentrations --- | --- rxn_offsets --- ] + /* :IntegrationResults: + * Organized data structure for accessing the integrator's output vector. + * Contents are MUTABLE! Updating the values in any containing pointers + * will be permanently reflected in the integrator's vector. + * + * All pointers in the structure point to different regions of the same vector. + * N_Vector: [ --- concentrations --- | ---- rxn_offsets ---- ] + */ struct IntegrationResults { // concentrations: bounded by [0, num_species) @@ -46,7 +53,7 @@ namespace Gillespy::TauHybrid public: N_Vector y; realtype t; - IntegrationResults integrate(double &t); + IntegrationResults integrate(double *t); IntegratorData data; Integrator(HybridSimulation *simulation); diff --git a/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/rhs.cpp b/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/rhs.cpp index 21f058f06..c5e850a40 100644 --- a/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/rhs.cpp +++ b/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/rhs.cpp @@ -57,26 +57,35 @@ int Gillespy::TauHybrid::rhs(realtype t, N_Vector y, N_Vector ydot, void *user_d // Process deterministic propensity state // These updates get written directly to the integrator's concentration state for (rxn_i = 0; rxn_i < num_reactions; ++rxn_i) { + propensity_tau = propensity_ode = 0.0; current_rxn = reactions[rxn_i].base_reaction; // NOTE: we may need to evaluate ODE and Tau propensities separately. // At the moment, it's unsure whether or not that's required. - propensity_ode = sim->propensity_function->ODEEvaluate(rxn_i, concentrations); - for (spec_i = 0; spec_i < num_species; ++spec_i) { - // Use the evaluated propensity to update the concentration levels and reaction state. - // Propensity is treated as positive if it's a product, negative if it's a reactant. - species_change = current_rxn->species_change[spec_i]; - if (species_change == 0) - continue; + switch (reactions[rxn_i].mode) { + case SimulationState::CONTINUOUS: + propensity_ode = sim->propensity_function->ODEEvaluate(rxn_i, concentrations); - // The product on the right evaluates to 1 if species_change is positive, - // and -1 if it's negative. - // This is a branchless alternative to using an if-statement. - dydt[spec_i] += propensity_ode * (-1 + 2 * (species_change > 0)); + for (spec_i = 0; spec_i < num_species; ++spec_i) { + // Use the evaluated propensity to update the concentration levels and reaction state. + // Propensity is treated as positive if it's a product, negative if it's a reactant. + species_change = current_rxn->species_change[spec_i]; + if (species_change == 0) + continue; + + // The product on the right evaluates to 1 if species_change is positive, + // and -1 if it's negative. + // This is a branchless alternative to using an if-statement. + dydt[spec_i] += propensity_ode * (-1 + 2 * (species_change > 0)); + } + case SimulationState::DISCRETE: + // Process stochastic reaction state by updating the root offset for each reaction. + propensity_tau = sim->propensity_function->TauEvaluate(rxn_i, populations); + break; + default: + break; } - // Process stochastic reaction state by updating the root offset for each reaction. - propensity_tau = sim->propensity_function->TauEvaluate(rxn_i, populations); // Integrate this reaction's rxn_offset forward using the propensity. dydt_offsets[rxn_i] += propensity_tau; // Propensity vector is only used for tau selection, so only Tau propensity is used. From b6c17cc4d4161f83548d1010dfd20ee616a20f47 Mon Sep 17 00:00:00 2001 From: Josh C Date: Tue, 15 Jun 2021 18:58:55 -0400 Subject: [PATCH 61/88] Add `reset` function to integrator - Resets the state of the integrator to a different time value - Requires `y0` to be permanently stored --- .../TauHybridSimulation.cpp | 5 ++- .../tau_hybrid_cpp_solver/TauHybridSolver.cpp | 45 +++++++------------ .../tau_hybrid_cpp_solver/integrator.cpp | 17 ++++++- .../c_base/tau_hybrid_cpp_solver/integrator.h | 3 +- .../cpp/c_base/tau_hybrid_cpp_solver/rhs.cpp | 18 ++++---- 5 files changed, 44 insertions(+), 44 deletions(-) diff --git a/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/TauHybridSimulation.cpp b/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/TauHybridSimulation.cpp index dc1bae433..57d8c0b12 100644 --- a/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/TauHybridSimulation.cpp +++ b/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/TauHybridSimulation.cpp @@ -49,7 +49,10 @@ int main(int argc, char* argv[]){ arg_stream >> end_time; break; case 'i': - map_variable_populations(arg_stream); + if (arg[2] == 'c') + arg_stream >> increment; + else if (arg[2] == 'i') + map_variable_populations(arg_stream); break; case 'p': map_variable_parameters(arg_stream); diff --git a/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/TauHybridSolver.cpp b/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/TauHybridSolver.cpp index 7dd7c5845..748d6e715 100644 --- a/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/TauHybridSolver.cpp +++ b/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/TauHybridSolver.cpp @@ -14,19 +14,6 @@ #include "tau.h" using namespace Gillespy; -struct IntegratorOptions{ - // CVODE constants returned if bad output, or success output. - // Constants: CV_SUCCESS, - // CV_MEM_NULL: CVODE memory block not initialized through call to CVodeCreate - // CV_NO_MALLOC: The allocation function CVodeInit not called - // CV_ILL_Input: An input tolerance was negative - int flag; - // absolute tolerace of a system - realtype abstol; - // relative tolerance of system - realtype reltol; - // double max_step; -}; namespace Gillespy::TauHybrid { bool interrupted = false; @@ -61,7 +48,7 @@ namespace Gillespy::TauHybrid { os << timeline[j] << ','; for (int k = 0; k < model->number_species; k++) { - os << trajectories_hybrid[i][j][k].discrete << ','; + os << trajectories_hybrid[i][j][k].continuous << ','; } } @@ -97,8 +84,6 @@ namespace Gillespy::TauHybrid { void TauHybridCSolver(HybridSimulation *simulation, const double tau_tol) { - //timeouts not supported right now?? - // signal(SIGINT, signalHandler); if (simulation == NULL) { return; } @@ -108,28 +93,25 @@ namespace Gillespy::TauHybrid { int num_reactions = model.number_reactions; int num_trajectories = simulation->number_trajectories; std::unique_ptr &species = model.species; - //TauArgs tau_args = initialize(*(simulation->model),tau_tol); double increment = simulation->timeline[1] - simulation->timeline[0]; // Tau selector initialization. Used to select a valid tau step. TauArgs tau_args = initialize(model, tau_tol); - // Population/concentration state values for each species. - // TODO: change back double -> hybrid_state, once we figure out how that works - std::vector current_state(num_species); - //copy initial state for each trajectory for(int s = 0; s < num_species; s++){ simulation->trajectories_hybrid[0][0][s].continuous = species[s].initial_population; - current_state[s] = species[s].initial_population; } + //Simulate for each trajectory for(int traj = 0; traj < num_trajectories; traj++){ - if (interrupted){ - break; - } + // Population/concentration state values for each species. + // TODO: change back double -> hybrid_state, once we figure out how that works + std::vector current_state(num_species); URNGenerator urn; + // The contents of y0 are "stolen" by the integrator. + // Do not attempt to directly use y0 after being passed to sol! N_Vector y0 = init_model_vector(model, urn); Integrator sol(simulation, y0, GPY_HYBRID_RELTOL, GPY_HYBRID_ABSTOL); @@ -138,7 +120,7 @@ namespace Gillespy::TauHybrid { current_state[spec_i] = species[spec_i].initial_population; } - // INITIALIZE INTEGRATOR STATE + // Represents the largest valid index of the output vector(s), y (and y0). int rxn_offset_boundary = num_species + num_reactions; // SIMULATION STEP LOOP @@ -184,9 +166,15 @@ namespace Gillespy::TauHybrid { // Use the current reaction_state to count the number of firings. // If a negative population is detected, then the loop breaks prematurely. bool invalid_state = false; + + // Start with the species concentration as a baseline value. + // Stochastic reactions will update populations relative to their concentrations. + for (int spec_i = 0; spec_i < num_species; ++spec_i) { + current_state[spec_i] = result.concentrations[spec_i]; + } + switch (sol.data.reaction_state[rxn_i].mode) { case SimulationState::DISCRETE: - // 0-initialize our population_changes array. for (int p_i = 0; p_i < num_species; ++p_i) population_changes[p_i] = 0; @@ -222,9 +210,6 @@ namespace Gillespy::TauHybrid { case SimulationState::CONTINUOUS: default: - for (int spec_i = 0; spec_i < num_species; ++spec_i) { - current_state[spec_i] += result.concentrations[spec_i]; - } break; } } diff --git a/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/integrator.cpp b/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/integrator.cpp index 9bc64de45..39bf49bb6 100644 --- a/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/integrator.cpp +++ b/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/integrator.cpp @@ -40,11 +40,18 @@ IntegratorData::~IntegratorData() Integrator::Integrator(HybridSimulation *simulation, N_Vector y0, double reltol, double abstol) - : y(y0), + : y0(y0), + y(N_VClone_Serial(y0)), data(simulation), num_reactions(simulation->model->number_reactions), num_species(simulation->model->number_species) { + // y0 is the initial state, y is updated during integration. + // N_VClone_Serial() does not clone *contents*, we have to do that explicitly. + for (int mem_i = 0; mem_i < num_reactions + num_species; ++mem_i) { + NV_Ith_S(y, mem_i) = NV_Ith_S(this->y0, mem_i); + } + for (int spec_i = 0; spec_i < num_species; ++spec_i) { data.populations[spec_i] = data.concentrations[spec_i] @@ -60,6 +67,12 @@ Integrator::Integrator(HybridSimulation *simulation, N_Vector y0, double reltol, validate(CVodeSetLinearSolver(cvode_mem, solver, NULL)); } +void Integrator::reset(double t_back) +{ + validate(CVodeReInit(cvode_mem, t_back, y0)); + t = t_back; +} + Integrator::~Integrator() { N_VDestroy_Serial(y); @@ -69,7 +82,7 @@ Integrator::~Integrator() IntegrationResults Integrator::integrate(double *t) { - if (!validate(CVode(cvode_mem, *t, y, t, CV_NORMAL))) { + if (!validate(CVode(cvode_mem, *t, y, &this->t, CV_NORMAL))) { return { nullptr, nullptr }; } diff --git a/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/integrator.h b/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/integrator.h index d71445256..732ea9322 100644 --- a/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/integrator.h +++ b/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/integrator.h @@ -47,16 +47,17 @@ namespace Gillespy::TauHybrid { private: void *cvode_mem; + N_Vector y0; SUNLinearSolver solver; int num_species; int num_reactions; public: N_Vector y; realtype t; + void reset(double t_back); IntegrationResults integrate(double *t); IntegratorData data; - Integrator(HybridSimulation *simulation); Integrator(HybridSimulation *simulation, N_Vector y0, double reltol, double abstol); ~Integrator(); }; diff --git a/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/rhs.cpp b/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/rhs.cpp index c5e850a40..8afb5bb36 100644 --- a/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/rhs.cpp +++ b/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/rhs.cpp @@ -13,7 +13,7 @@ int Gillespy::TauHybrid::rhs(realtype t, N_Vector y, N_Vector ydot, void *user_d // Get y(t) vector and f(t, y) vector realtype *Y = N_VGetArrayPointer(y); realtype *dydt = N_VGetArrayPointer(ydot); - realtype propensity_ode, propensity_tau; + realtype propensity; // Extract simulation data IntegratorData *data = static_cast(user_data); @@ -57,14 +57,14 @@ int Gillespy::TauHybrid::rhs(realtype t, N_Vector y, N_Vector ydot, void *user_d // Process deterministic propensity state // These updates get written directly to the integrator's concentration state for (rxn_i = 0; rxn_i < num_reactions; ++rxn_i) { - propensity_tau = propensity_ode = 0.0; + propensity = 0.0; current_rxn = reactions[rxn_i].base_reaction; // NOTE: we may need to evaluate ODE and Tau propensities separately. // At the moment, it's unsure whether or not that's required. switch (reactions[rxn_i].mode) { case SimulationState::CONTINUOUS: - propensity_ode = sim->propensity_function->ODEEvaluate(rxn_i, concentrations); + propensity = sim->propensity_function->ODEEvaluate(rxn_i, data->concentrations); for (spec_i = 0; spec_i < num_species; ++spec_i) { // Use the evaluated propensity to update the concentration levels and reaction state. @@ -76,20 +76,18 @@ int Gillespy::TauHybrid::rhs(realtype t, N_Vector y, N_Vector ydot, void *user_d // The product on the right evaluates to 1 if species_change is positive, // and -1 if it's negative. // This is a branchless alternative to using an if-statement. - dydt[spec_i] += propensity_ode * (-1 + 2 * (species_change > 0)); + dydt[spec_i] += propensity * (-1 + 2 * (species_change > 0)); } + break; case SimulationState::DISCRETE: // Process stochastic reaction state by updating the root offset for each reaction. - propensity_tau = sim->propensity_function->TauEvaluate(rxn_i, populations); + propensity = sim->propensity_function->TauEvaluate(rxn_i, data->populations); + dydt_offsets[rxn_i] += propensity; + propensities[rxn_i] = propensity; break; default: break; } - - // Integrate this reaction's rxn_offset forward using the propensity. - dydt_offsets[rxn_i] += propensity_tau; - // Propensity vector is only used for tau selection, so only Tau propensity is used. - propensities[rxn_i] = propensity_tau; } return 0; From 32502101bd1afc46d5be088bb96ea08eb80c5e1d Mon Sep 17 00:00:00 2001 From: Josh C Date: Wed, 16 Jun 2021 13:22:41 -0400 Subject: [PATCH 62/88] Clean up hybrid simulation definitions - Move definitions in `HybridModel.h` from solver file to new `HybridModel.cpp` file - Add additional Makefile targets for Tau dependencies --- gillespy2/solvers/cpp/c_base/Makefile | 20 +++++- .../tau_hybrid_cpp_solver/HybridModel.cpp | 66 +++++++++++++++++++ .../tau_hybrid_cpp_solver/TauHybridSolver.cpp | 60 ----------------- 3 files changed, 83 insertions(+), 63 deletions(-) create mode 100644 gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/HybridModel.cpp diff --git a/gillespy2/solvers/cpp/c_base/Makefile b/gillespy2/solvers/cpp/c_base/Makefile index efd234b71..5a5c11d61 100644 --- a/gillespy2/solvers/cpp/c_base/Makefile +++ b/gillespy2/solvers/cpp/c_base/Makefile @@ -32,6 +32,12 @@ sundials_linearsolver.o sundials_nonlinearsolver.o sundials_nvector_senswrapper. sundials_nvector.o nvector_serial.o cvode.o cvode_spils.o sundials_math.o sunlinsol_spgmr.o SUNOBJ_PATHS := $(SUNOBJ:%.o=$(SUNDIALS_OBJ)/%.o) +TAU_DEPENDENCIES = tau.o +TAU_DEPS_PATHS := $(TAU_DEPENDENCIES:%.o=$(OBJ_DIR)/%.o) + +TAU_HYBRID_DEPENDENCIES = HybridModel.o rhs.o integrator.o +TAU_HYBRID_DEPS_PATHS := $(TAU_HYBRID_DEPENDENCIES:%.o=$(OBJ_DIR)/%.o) + ################################### ### SOLVER DEPENDENCIES COMPILE ### $(OBJ_DIR)/model.o: $(CBASE_DIR)/model.cpp $(CBASE_DIR)/model.h @@ -40,7 +46,15 @@ model: $(OBJ_DIR)/model.o ; $(SUNOBJ_PATHS): $(SUNDIALS_OBJ)/%.o: $(SUNDIALS_SRC)/%.c $(CC) -c -o $@ $< $(CXXFLAGS) -I$(SUNDIALS_INC) -sundials: $(SUNOBJ_PATHS) ; +sundials: $(SUNOBJ_PATHS); + +$(TAU_DEPS_PATHS): $(OBJ_DIR)/%.o: $(TAU_DIR)/%.cpp + $(CC) -c -o $@ $< $(CXXFLAGS) $(INCLUDES) +tau_deps: $(TAU_DEPS_PATHS); + +$(TAU_HYBRID_DEPS_PATHS): $(OBJ_DIR)/%.o: $(TAU_HYBRID_SOLVER_PATH)/%.cpp + $(CC) -c -o $@ $< $(CXXFLAGS) $(INCLUDES) +hybrid_deps: $(TAU_HYBRID_DEPS_PATHS); Tau.o: $(TAU_DIR)/tau.cpp $(TAU_DIR)/tau.h $(CC) $(CXXFLAGS) -c -o Tau.o $(TAU_DIR)/tau.cpp $(INCLUDES) @@ -101,8 +115,8 @@ ssa: SSASimulation.o SSASolver.o model $(TEMPLATE_CPP) tau_leap: TauLeapingSimulation.o TauLeapingSolver.o model $(TEMPLATE_CPP) $(CC) $(COMPILATION_ARGS) $(OBJ_DIR)/TauLeapingSimulation.o $(OBJ_DIR)/TauLeapingSolver.o -hybrid: TauHybridSimulation.o TauHybridSolver.o model sundials $(TAU_DIR)/tau.cpp $(TAU_HYBRID_SOLVER_PATH)/integrator.cpp $(TAU_HYBRID_SOLVER_PATH)/rhs.cpp $(TEMPLATE_CPP) - $(CC) $(COMPILATION_ARGS) $(SUNOBJ_PATHS) $(TAU_HYBRID_SOLVER_PATH)/integrator.cpp $(TAU_HYBRID_SOLVER_PATH)/rhs.cpp $(TAU_DIR)/tau.cpp $(OBJ_DIR)/TauHybridSimulation.o $(OBJ_DIR)/TauHybridSolver.o +hybrid: TauHybridSimulation.o TauHybridSolver.o model sundials tau_deps hybrid_deps $(TEMPLATE_CPP) + $(CC) $(COMPILATION_ARGS) $(SUNOBJ_PATHS) $(TAU_HYBRID_DEPS_PATHS) $(TAU_DEPS_PATHS) $(OBJ_DIR)/TauHybridSimulation.o $(OBJ_DIR)/TauHybridSolver.o build: $(SOLVER); diff --git a/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/HybridModel.cpp b/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/HybridModel.cpp new file mode 100644 index 000000000..be7549cfc --- /dev/null +++ b/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/HybridModel.cpp @@ -0,0 +1,66 @@ +#include "HybridModel.h" + +namespace Gillespy::TauHybrid +{ + + void simulation_hybrid_init(HybridSimulation &simulation) + { + Model *model = simulation.model; + init_timeline(simulation); + simulation.type = HYBRID; + + unsigned int trajectory_size = simulation.number_timesteps * (model -> number_species); + /* 1-dimensional arrays might be unnecessary; look into mapping 3D array directly */ + simulation.trajectories_hybrid1D = new hybrid_state[simulation.number_trajectories * trajectory_size]; + simulation.trajectories_hybrid = new hybrid_state**[simulation.number_trajectories]; + + for(unsigned int i = 0; i < simulation.number_trajectories; i++){ + simulation.trajectories_hybrid[i] = new hybrid_state*[simulation.number_timesteps]; + for(unsigned int j = 0; j < simulation.number_timesteps; j++){ + simulation.trajectories_hybrid[i][j] = &(simulation.trajectories_hybrid1D[i * trajectory_size + j * (model -> number_species)]); + } + } + } + + void HybridSimulation::output_hybrid_results(std::ostream &os) + { + for (int i = 0 ; i < number_trajectories; i++){ + for (int j = 0; j < number_timesteps; j++){ + os << timeline[j] << ','; + + for (int k = 0; k < model->number_species; k++) { + os << trajectories_hybrid[i][j][k].continuous << ','; + } + } + + os<<(int)current_time; + } + } + + HybridReaction::HybridReaction() + : mode(SimulationState::CONTINUOUS), + base_reaction(nullptr) + { + // Empty constructor body + } + + HybridSpecies::HybridSpecies() + : user_mode(SimulationState::CONTINUOUS), + partition_mode(SimulationState::CONTINUOUS), + switch_tol(0.03), + switch_min(0) + { + // Empty constructor body + } + + HybridSimulation::~HybridSimulation() + { + if (type == HYBRID) { + for(unsigned int i = 0; i < number_trajectories; i++){ + delete trajectories_hybrid[i]; + } + delete trajectories_hybrid; + } + } + +} diff --git a/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/TauHybridSolver.cpp b/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/TauHybridSolver.cpp index 748d6e715..c32f02fd3 100644 --- a/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/TauHybridSolver.cpp +++ b/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/TauHybridSolver.cpp @@ -22,66 +22,6 @@ namespace Gillespy::TauHybrid { interrupted = true; } - void simulation_hybrid_init(HybridSimulation &simulation) - { - Model *model = simulation.model; - init_timeline(simulation); - simulation.type = HYBRID; - - unsigned int trajectory_size = simulation.number_timesteps * (model -> number_species); - /* 1-dimensional arrays might be unnecessary; look into mapping 3D array directly */ - simulation.trajectories_hybrid1D = new hybrid_state[simulation.number_trajectories * trajectory_size]; - simulation.trajectories_hybrid = new hybrid_state**[simulation.number_trajectories]; - - for(unsigned int i = 0; i < simulation.number_trajectories; i++){ - simulation.trajectories_hybrid[i] = new hybrid_state*[simulation.number_timesteps]; - for(unsigned int j = 0; j < simulation.number_timesteps; j++){ - simulation.trajectories_hybrid[i][j] = &(simulation.trajectories_hybrid1D[i * trajectory_size + j * (model -> number_species)]); - } - } - } - - void HybridSimulation::output_hybrid_results(std::ostream &os) - { - for (int i = 0 ; i < number_trajectories; i++){ - for (int j = 0; j < number_timesteps; j++){ - os << timeline[j] << ','; - - for (int k = 0; k < model->number_species; k++) { - os << trajectories_hybrid[i][j][k].continuous << ','; - } - } - - os<<(int)current_time; - } - } - - HybridReaction::HybridReaction() - : mode(SimulationState::CONTINUOUS), - base_reaction(nullptr) - { - // Empty constructor body - } - - HybridSpecies::HybridSpecies() - : user_mode(SimulationState::CONTINUOUS), - partition_mode(SimulationState::CONTINUOUS), - switch_tol(0.03), - switch_min(0) - { - // Empty constructor body - } - - HybridSimulation::~HybridSimulation() - { - if (type == HYBRID) { - for(unsigned int i = 0; i < number_trajectories; i++){ - delete trajectories_hybrid[i]; - } - delete trajectories_hybrid; - } - } - void TauHybridCSolver(HybridSimulation *simulation, const double tau_tol) { if (simulation == NULL) { From d923831cbdbdfed64a2cc30935f2b78e117e948d Mon Sep 17 00:00:00 2001 From: Josh C Date: Wed, 16 Jun 2021 19:56:09 -0400 Subject: [PATCH 63/88] Invalid state loop - Groundwork for integrator to repeat until state valid - Reorder RHS switch statement to always process continuous reactions --- .../tau_hybrid_cpp_solver/HybridModel.cpp | 8 +- .../tau_hybrid_cpp_solver/TauHybridSolver.cpp | 92 +++++++++---------- .../cpp/c_base/tau_hybrid_cpp_solver/rhs.cpp | 12 +-- 3 files changed, 56 insertions(+), 56 deletions(-) diff --git a/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/HybridModel.cpp b/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/HybridModel.cpp index be7549cfc..be4e8ea97 100644 --- a/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/HybridModel.cpp +++ b/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/HybridModel.cpp @@ -29,7 +29,7 @@ namespace Gillespy::TauHybrid os << timeline[j] << ','; for (int k = 0; k < model->number_species; k++) { - os << trajectories_hybrid[i][j][k].continuous << ','; + os << trajectories_hybrid[i][j][k].discrete << ','; } } @@ -38,15 +38,15 @@ namespace Gillespy::TauHybrid } HybridReaction::HybridReaction() - : mode(SimulationState::CONTINUOUS), + : mode(SimulationState::DISCRETE), base_reaction(nullptr) { // Empty constructor body } HybridSpecies::HybridSpecies() - : user_mode(SimulationState::CONTINUOUS), - partition_mode(SimulationState::CONTINUOUS), + : user_mode(SimulationState::DISCRETE), + partition_mode(SimulationState::DISCRETE), switch_tol(0.03), switch_min(0) { diff --git a/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/TauHybridSolver.cpp b/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/TauHybridSolver.cpp index c32f02fd3..31510af39 100644 --- a/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/TauHybridSolver.cpp +++ b/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/TauHybridSolver.cpp @@ -95,64 +95,64 @@ namespace Gillespy::TauHybrid { // flag = CVode(cvode_mem, next_time, y0, &next_time, CV_NORMAL); IntegrationResults result = sol.integrate(&next_time); - // The newly-updated reaction_states vector may need to be reconciled now. - // A positive reaction_state means reactions have potentially fired. - // NOTE: it is possible for a population to swing negative, where a smaller Tau is needed. - for (int rxn_i = 0; rxn_i < num_reactions; ++rxn_i) { - // Temporary variable for the reaction's state. - // Does not get updated unless the changes are deemed valid. - double rxn_state = result.reactions[rxn_i]; - - // Use the current reaction_state to count the number of firings. - // If a negative population is detected, then the loop breaks prematurely. - bool invalid_state = false; - - // Start with the species concentration as a baseline value. - // Stochastic reactions will update populations relative to their concentrations. + // Start with the species concentration as a baseline value. + // Stochastic reactions will update populations relative to their concentrations. + + bool invalid_state = false; + do { for (int spec_i = 0; spec_i < num_species; ++spec_i) { current_state[spec_i] = result.concentrations[spec_i]; } - - switch (sol.data.reaction_state[rxn_i].mode) { - case SimulationState::DISCRETE: - // 0-initialize our population_changes array. - for (int p_i = 0; p_i < num_species; ++p_i) - population_changes[p_i] = 0; - - while (rxn_state >= 0 && !invalid_state) { - // "Fire" a reaction by recording changes in dependent species. - // If a negative value is detected, break without saving changes. - for (int spec_i = 0; spec_i < num_species; ++spec_i) { - population_changes[spec_i] += - model.reactions[rxn_i].species_change[spec_i]; - if (current_state[spec_i] + population_changes[spec_i] < 0) { - invalid_state = true; + // The newly-updated reaction_states vector may need to be reconciled now. + // A positive reaction_state means reactions have potentially fired. + // NOTE: it is possible for a population to swing negative, where a smaller Tau is needed. + for (int rxn_i = 0; rxn_i < num_reactions; ++rxn_i) { + // Temporary variable for the reaction's state. + // Does not get updated unless the changes are deemed valid. + double rxn_state = result.reactions[rxn_i]; + + switch (sol.data.reaction_state[rxn_i].mode) { + case SimulationState::DISCRETE: + // 0-initialize our population_changes array. + for (int p_i = 0; p_i < num_species; ++p_i) + population_changes[p_i] = 0; + + while (rxn_state >= 0 && !invalid_state) { + // "Fire" a reaction by recording changes in dependent species. + // If a negative value is detected, break without saving changes. + for (int spec_i = 0; spec_i < num_species; ++spec_i) { + population_changes[spec_i] += + model.reactions[rxn_i].species_change[spec_i]; + if (current_state[spec_i] + population_changes[spec_i] < 0) { + invalid_state = true; + } } - } - rxn_state += log(urn.next()); - } + rxn_state += log(urn.next()); + } + result.reactions[rxn_i] = rxn_state; + break; - // Positive reaction state means a negative population was detected. - // Only update state with the given population changes if valid. - if (invalid_state) { - // TODO: invalidate reaction timestep, reset integrator state, try again - std::cerr << "Integration step failed; RIP" << std::endl; - continue; + case SimulationState::CONTINUOUS: + default: + break; } + } + // Positive reaction state means a negative population was detected. + // Only update state with the given population changes if valid. + if (invalid_state) { + // TODO: invalidate reaction timestep, reset integrator state, try again + std::cerr << "Integration step failed; RIP" << std::endl; + invalid_state = false; + } + else { // "Permanently" update the rxn_state and populations. for (int p_i = 0; p_i < num_species; ++p_i) { current_state[p_i] += population_changes[p_i]; } - result.reactions[rxn_i] = rxn_state; - break; - - case SimulationState::CONTINUOUS: - default: - break; } - } + } while (invalid_state); // Output the results for this time step. simulation->current_time = next_time; @@ -160,7 +160,7 @@ namespace Gillespy::TauHybrid { while (save_time <= next_time) { // Write each species, one at a time (from ODE solution) for (int spec_i = 0; spec_i < num_species; ++spec_i) { - simulation->trajectories_hybrid[traj][save_time][spec_i].continuous = current_state[spec_i]; + simulation->trajectories_hybrid[traj][save_time][spec_i].discrete = current_state[spec_i]; } save_time += increment; } diff --git a/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/rhs.cpp b/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/rhs.cpp index 8afb5bb36..8c01e3283 100644 --- a/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/rhs.cpp +++ b/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/rhs.cpp @@ -63,6 +63,12 @@ int Gillespy::TauHybrid::rhs(realtype t, N_Vector y, N_Vector ydot, void *user_d // At the moment, it's unsure whether or not that's required. switch (reactions[rxn_i].mode) { + case SimulationState::DISCRETE: + // Process stochastic reaction state by updating the root offset for each reaction. + propensity = sim->propensity_function->TauEvaluate(rxn_i, data->populations); + dydt_offsets[rxn_i] += propensity; + propensities[rxn_i] = propensity; + // break; left out on purpose, continuous results happen no matter what case SimulationState::CONTINUOUS: propensity = sim->propensity_function->ODEEvaluate(rxn_i, data->concentrations); @@ -79,12 +85,6 @@ int Gillespy::TauHybrid::rhs(realtype t, N_Vector y, N_Vector ydot, void *user_d dydt[spec_i] += propensity * (-1 + 2 * (species_change > 0)); } break; - case SimulationState::DISCRETE: - // Process stochastic reaction state by updating the root offset for each reaction. - propensity = sim->propensity_function->TauEvaluate(rxn_i, data->populations); - dydt_offsets[rxn_i] += propensity; - propensities[rxn_i] = propensity; - break; default: break; } From 804e05c2e79942736c7501901efd8a76959ddf69 Mon Sep 17 00:00:00 2001 From: Joshua Cooper Date: Thu, 17 Jun 2021 15:05:50 -0400 Subject: [PATCH 64/88] Hybrid output moved to float-only - Remove `hybrid_state` union from output - Expects output to be written as double regardless of original value --- gillespy2/solvers/cpp/c_base/model.cpp | 2 +- gillespy2/solvers/cpp/c_base/model.h | 4 +- .../tau_hybrid_cpp_solver/HybridModel.cpp | 44 +++---------------- .../tau_hybrid_cpp_solver/HybridModel.h | 3 -- .../TauHybridSimulation.cpp | 4 +- .../tau_hybrid_cpp_solver/TauHybridSolver.cpp | 4 +- 6 files changed, 12 insertions(+), 49 deletions(-) diff --git a/gillespy2/solvers/cpp/c_base/model.cpp b/gillespy2/solvers/cpp/c_base/model.cpp index 9a4fe9340..34f827587 100644 --- a/gillespy2/solvers/cpp/c_base/model.cpp +++ b/gillespy2/solvers/cpp/c_base/model.cpp @@ -129,7 +129,7 @@ namespace Gillespy{ for (int j = 0; j < number_timesteps; j++){ os<number_species; k++){ - if (type == ODE){ + if (type == ODE || type == HYBRID){ os< number_species); - /* 1-dimensional arrays might be unnecessary; look into mapping 3D array directly */ - simulation.trajectories_hybrid1D = new hybrid_state[simulation.number_trajectories * trajectory_size]; - simulation.trajectories_hybrid = new hybrid_state**[simulation.number_trajectories]; - - for(unsigned int i = 0; i < simulation.number_trajectories; i++){ - simulation.trajectories_hybrid[i] = new hybrid_state*[simulation.number_timesteps]; - for(unsigned int j = 0; j < simulation.number_timesteps; j++){ - simulation.trajectories_hybrid[i][j] = &(simulation.trajectories_hybrid1D[i * trajectory_size + j * (model -> number_species)]); - } - } - } - - void HybridSimulation::output_hybrid_results(std::ostream &os) - { - for (int i = 0 ; i < number_trajectories; i++){ - for (int j = 0; j < number_timesteps; j++){ - os << timeline[j] << ','; - - for (int k = 0; k < model->number_species; k++) { - os << trajectories_hybrid[i][j][k].discrete << ','; - } - } - - os<<(int)current_time; - } - } - HybridReaction::HybridReaction() - : mode(SimulationState::DISCRETE), + : mode(SimulationState::CONTINUOUS), base_reaction(nullptr) { // Empty constructor body } HybridSpecies::HybridSpecies() - : user_mode(SimulationState::DISCRETE), - partition_mode(SimulationState::DISCRETE), + : user_mode(SimulationState::CONTINUOUS), + partition_mode(SimulationState::CONTINUOUS), switch_tol(0.03), switch_min(0) { @@ -57,9 +23,9 @@ namespace Gillespy::TauHybrid { if (type == HYBRID) { for(unsigned int i = 0; i < number_trajectories; i++){ - delete trajectories_hybrid[i]; + delete trajectoriesODE[i]; } - delete trajectories_hybrid; + delete trajectoriesODE; } } diff --git a/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/HybridModel.h b/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/HybridModel.h index 985e5269a..28c1eba7a 100644 --- a/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/HybridModel.h +++ b/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/HybridModel.h @@ -63,12 +63,9 @@ namespace Gillespy::TauHybrid { hybrid_state *trajectories_hybrid1D; hybrid_state ***trajectories_hybrid; - void output_hybrid_results(std::ostream &os); double hybrid_propensity(int reaction_id, std::vector ¤t_state); ~HybridSimulation(); }; - void simulation_hybrid_init(HybridSimulation &simulation); - } diff --git a/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/TauHybridSimulation.cpp b/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/TauHybridSimulation.cpp index 57d8c0b12..4c9567551 100644 --- a/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/TauHybridSimulation.cpp +++ b/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/TauHybridSimulation.cpp @@ -86,10 +86,10 @@ int main(int argc, char* argv[]){ simulation.number_timesteps = number_timesteps; simulation.number_trajectories = number_trajectories; simulation.propensity_function = propFun; - TauHybrid::simulation_hybrid_init(simulation); + simulationINIT(&model, simulation); // Perform ODE // TauHybrid::TauHybridCSolver(&simulation, tau_tol); - simulation.output_hybrid_results(std :: cout); + simulation.output_results_buffer(std::cout); delete propFun; return 0; } diff --git a/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/TauHybridSolver.cpp b/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/TauHybridSolver.cpp index 31510af39..5e08e668a 100644 --- a/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/TauHybridSolver.cpp +++ b/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/TauHybridSolver.cpp @@ -40,7 +40,7 @@ namespace Gillespy::TauHybrid { //copy initial state for each trajectory for(int s = 0; s < num_species; s++){ - simulation->trajectories_hybrid[0][0][s].continuous = species[s].initial_population; + simulation->trajectoriesODE[0][0][s] = species[s].initial_population; } //Simulate for each trajectory @@ -160,7 +160,7 @@ namespace Gillespy::TauHybrid { while (save_time <= next_time) { // Write each species, one at a time (from ODE solution) for (int spec_i = 0; spec_i < num_species; ++spec_i) { - simulation->trajectories_hybrid[traj][save_time][spec_i].discrete = current_state[spec_i]; + simulation->trajectoriesODE[traj][save_time][spec_i] = current_state[spec_i]; } save_time += increment; } From ec123741706f7f4ee1f752b679bf070948785131 Mon Sep 17 00:00:00 2001 From: Joshua Cooper Date: Thu, 17 Jun 2021 18:16:53 -0400 Subject: [PATCH 65/88] "Valid" integrator reset + add Tau propensities - `reset()` method in integrator, resets just to "refresh" values in vector - Currently does not work for invalid reactions - Fix `TauEvaluate()` to actually map propensity values --- .../tau_hybrid_cpp_solver/HybridModel.cpp | 6 ++-- .../TauHybridSimulation.cpp | 4 ++- .../tau_hybrid_cpp_solver/TauHybridSolver.cpp | 29 +++++++++++-------- .../tau_hybrid_cpp_solver/integrator.cpp | 2 +- .../cpp/c_base/tau_hybrid_cpp_solver/rhs.cpp | 10 +++---- 5 files changed, 29 insertions(+), 22 deletions(-) diff --git a/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/HybridModel.cpp b/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/HybridModel.cpp index 4b73be629..25214b92f 100644 --- a/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/HybridModel.cpp +++ b/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/HybridModel.cpp @@ -4,15 +4,15 @@ namespace Gillespy::TauHybrid { HybridReaction::HybridReaction() - : mode(SimulationState::CONTINUOUS), + : mode(SimulationState::DISCRETE), base_reaction(nullptr) { // Empty constructor body } HybridSpecies::HybridSpecies() - : user_mode(SimulationState::CONTINUOUS), - partition_mode(SimulationState::CONTINUOUS), + : user_mode(SimulationState::DISCRETE), + partition_mode(SimulationState::DISCRETE), switch_tol(0.03), switch_min(0) { diff --git a/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/TauHybridSimulation.cpp b/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/TauHybridSimulation.cpp index 4c9567551..a68094740 100644 --- a/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/TauHybridSimulation.cpp +++ b/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/TauHybridSimulation.cpp @@ -22,7 +22,9 @@ class PropensityFunction : public IPropensityFunction{ double ODEEvaluate(int reaction_number, const std::vector &S){ return map_ode_propensity(reaction_number, S); } - double TauEvaluate(unsigned int reaction_number, const std::vector &S){return 1.0;} + double TauEvaluate(unsigned int reaction_number, const std::vector &S) { + return map_propensity(reaction_number, S); + } double evaluate(unsigned int reaction_number, unsigned int* S){return 1.0;} }; diff --git a/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/TauHybridSolver.cpp b/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/TauHybridSolver.cpp index 5e08e668a..a976b20bf 100644 --- a/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/TauHybridSolver.cpp +++ b/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/TauHybridSolver.cpp @@ -48,6 +48,7 @@ namespace Gillespy::TauHybrid { // Population/concentration state values for each species. // TODO: change back double -> hybrid_state, once we figure out how that works std::vector current_state(num_species); + std::vector current_populations(num_species); URNGenerator urn; // The contents of y0 are "stolen" by the integrator. @@ -58,6 +59,7 @@ namespace Gillespy::TauHybrid { // Initialize the species population for the trajectory. for (int spec_i = 0; spec_i < num_species; ++spec_i) { current_state[spec_i] = species[spec_i].initial_population; + current_populations[spec_i] = species[spec_i].initial_population; } // Represents the largest valid index of the output vector(s), y (and y0). @@ -66,7 +68,7 @@ namespace Gillespy::TauHybrid { // SIMULATION STEP LOOP double next_time; double tau_step = 0.0; - int save_time = simulation->timeline[1]; + double save_time = simulation->timeline[1]; // Temporary array to store changes to dependent species. // Should be 0-initialized each time it's used. @@ -81,9 +83,14 @@ namespace Gillespy::TauHybrid { simulation->current_time, save_time, sol.data.propensities, - sol.data.populations + current_populations ); + // 0-initialize our population_changes array. + for (int p_i = 0; p_i < num_species; ++p_i) { + population_changes[p_i] = 0; + } + // Determine what the next time point is. // This will become current_time on the next iteration. // If a retry with a smaller tau_step is deemed necessary, this will change. @@ -94,15 +101,14 @@ namespace Gillespy::TauHybrid { // For stochastic reactions, integration updates the rxn_offsets vector. // flag = CVode(cvode_mem, next_time, y0, &next_time, CV_NORMAL); IntegrationResults result = sol.integrate(&next_time); - - // Start with the species concentration as a baseline value. - // Stochastic reactions will update populations relative to their concentrations. - bool invalid_state = false; do { + // Start with the species concentration as a baseline value. + // Stochastic reactions will update populations relative to their concentrations. for (int spec_i = 0; spec_i < num_species; ++spec_i) { current_state[spec_i] = result.concentrations[spec_i]; } + // The newly-updated reaction_states vector may need to be reconciled now. // A positive reaction_state means reactions have potentially fired. // NOTE: it is possible for a population to swing negative, where a smaller Tau is needed. @@ -113,11 +119,7 @@ namespace Gillespy::TauHybrid { switch (sol.data.reaction_state[rxn_i].mode) { case SimulationState::DISCRETE: - // 0-initialize our population_changes array. - for (int p_i = 0; p_i < num_species; ++p_i) - population_changes[p_i] = 0; - - while (rxn_state >= 0 && !invalid_state) { + while (rxn_state >= 0) { // "Fire" a reaction by recording changes in dependent species. // If a negative value is detected, break without saving changes. for (int spec_i = 0; spec_i < num_species; ++spec_i) { @@ -150,17 +152,20 @@ namespace Gillespy::TauHybrid { // "Permanently" update the rxn_state and populations. for (int p_i = 0; p_i < num_species; ++p_i) { current_state[p_i] += population_changes[p_i]; + current_populations[p_i] = current_state[p_i]; + result.concentrations[p_i] = current_state[p_i]; } } } while (invalid_state); // Output the results for this time step. + sol.reset(next_time); simulation->current_time = next_time; while (save_time <= next_time) { // Write each species, one at a time (from ODE solution) for (int spec_i = 0; spec_i < num_species; ++spec_i) { - simulation->trajectoriesODE[traj][save_time][spec_i] = current_state[spec_i]; + simulation->trajectoriesODE[traj][(int) save_time][spec_i] = current_state[spec_i]; } save_time += increment; } diff --git a/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/integrator.cpp b/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/integrator.cpp index 39bf49bb6..c359dc0fd 100644 --- a/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/integrator.cpp +++ b/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/integrator.cpp @@ -69,7 +69,7 @@ Integrator::Integrator(HybridSimulation *simulation, N_Vector y0, double reltol, void Integrator::reset(double t_back) { - validate(CVodeReInit(cvode_mem, t_back, y0)); + validate(CVodeReInit(cvode_mem, t_back, y)); t = t_back; } diff --git a/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/rhs.cpp b/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/rhs.cpp index 8c01e3283..4be8b783d 100644 --- a/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/rhs.cpp +++ b/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/rhs.cpp @@ -38,7 +38,8 @@ int Gillespy::TauHybrid::rhs(realtype t, N_Vector y, N_Vector ydot, void *user_d // dy/dt results are initialized to zero, and become the change in propensity. unsigned int spec_i; for (spec_i = 0; spec_i < num_species; ++spec_i) { - populations[spec_i] = concentrations[spec_i] = Y[spec_i]; + concentrations[spec_i] = Y[spec_i]; + populations[spec_i] = Y[spec_i]; dydt[spec_i] = 0; } @@ -57,7 +58,6 @@ int Gillespy::TauHybrid::rhs(realtype t, N_Vector y, N_Vector ydot, void *user_d // Process deterministic propensity state // These updates get written directly to the integrator's concentration state for (rxn_i = 0; rxn_i < num_reactions; ++rxn_i) { - propensity = 0.0; current_rxn = reactions[rxn_i].base_reaction; // NOTE: we may need to evaluate ODE and Tau propensities separately. // At the moment, it's unsure whether or not that's required. @@ -65,12 +65,12 @@ int Gillespy::TauHybrid::rhs(realtype t, N_Vector y, N_Vector ydot, void *user_d switch (reactions[rxn_i].mode) { case SimulationState::DISCRETE: // Process stochastic reaction state by updating the root offset for each reaction. - propensity = sim->propensity_function->TauEvaluate(rxn_i, data->populations); + propensity = sim->propensity_function->TauEvaluate(rxn_i, populations); dydt_offsets[rxn_i] += propensity; - propensities[rxn_i] = propensity; // break; left out on purpose, continuous results happen no matter what case SimulationState::CONTINUOUS: - propensity = sim->propensity_function->ODEEvaluate(rxn_i, data->concentrations); + propensity = sim->propensity_function->ODEEvaluate(rxn_i, concentrations); + propensities[rxn_i] = propensity; for (spec_i = 0; spec_i < num_species; ++spec_i) { // Use the evaluated propensity to update the concentration levels and reaction state. From 8bd79dcb7020b04a48ecc1841314fe003a24c980 Mon Sep 17 00:00:00 2001 From: Josh C Date: Fri, 18 Jun 2021 15:14:13 -0400 Subject: [PATCH 66/88] Primitive integrator reset - Save integrator state on each iteration - Restore integrator state on detection of invalid Tau step - Currently does not implement Hybrid SSA direct rootfinding yet --- .../tau_hybrid_cpp_solver/TauHybridSolver.cpp | 34 +++++++++++-------- .../tau_hybrid_cpp_solver/integrator.cpp | 27 +++++++++++++-- .../c_base/tau_hybrid_cpp_solver/integrator.h | 25 +++++++++++++- .../cpp/c_base/tau_hybrid_cpp_solver/rhs.cpp | 3 +- 4 files changed, 69 insertions(+), 20 deletions(-) diff --git a/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/TauHybridSolver.cpp b/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/TauHybridSolver.cpp index a976b20bf..bba4ef1c3 100644 --- a/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/TauHybridSolver.cpp +++ b/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/TauHybridSolver.cpp @@ -86,23 +86,28 @@ namespace Gillespy::TauHybrid { current_populations ); - // 0-initialize our population_changes array. - for (int p_i = 0; p_i < num_species; ++p_i) { - population_changes[p_i] = 0; - } - // Determine what the next time point is. // This will become current_time on the next iteration. // If a retry with a smaller tau_step is deemed necessary, this will change. next_time = simulation->current_time + tau_step; - // Integration Step - // For deterministic reactions, the concentrations are updated directly. - // For stochastic reactions, integration updates the rxn_offsets vector. - // flag = CVode(cvode_mem, next_time, y0, &next_time, CV_NORMAL); - IntegrationResults result = sol.integrate(&next_time); - bool invalid_state = false; + // The integration loop continues until a valid solution is found. + // Any invalid Tau steps (which cause negative populations) are discarded. + sol.save_state(); + bool invalid_state; do { + invalid_state = false; + // Integration Step + // For deterministic reactions, the concentrations are updated directly. + // For stochastic reactions, integration updates the rxn_offsets vector. + // flag = CVode(cvode_mem, next_time, y0, &next_time, CV_NORMAL); + IntegrationResults result = sol.integrate(&next_time); + + // 0-initialize our population_changes array. + for (int p_i = 0; p_i < num_species; ++p_i) { + population_changes[p_i] = 0; + } + // Start with the species concentration as a baseline value. // Stochastic reactions will update populations relative to their concentrations. for (int spec_i = 0; spec_i < num_species; ++spec_i) { @@ -144,9 +149,8 @@ namespace Gillespy::TauHybrid { // Positive reaction state means a negative population was detected. // Only update state with the given population changes if valid. if (invalid_state) { - // TODO: invalidate reaction timestep, reset integrator state, try again - std::cerr << "Integration step failed; RIP" << std::endl; - invalid_state = false; + sol.restore_state(); + next_time = simulation->current_time + (tau_step / 2); } else { // "Permanently" update the rxn_state and populations. @@ -159,7 +163,7 @@ namespace Gillespy::TauHybrid { } while (invalid_state); // Output the results for this time step. - sol.reset(next_time); + sol.refresh_state(); simulation->current_time = next_time; while (save_time <= next_time) { diff --git a/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/integrator.cpp b/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/integrator.cpp index c359dc0fd..0dc76d138 100644 --- a/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/integrator.cpp +++ b/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/integrator.cpp @@ -67,10 +67,31 @@ Integrator::Integrator(HybridSimulation *simulation, N_Vector y0, double reltol, validate(CVodeSetLinearSolver(cvode_mem, solver, NULL)); } -void Integrator::reset(double t_back) +double Integrator::save_state() { - validate(CVodeReInit(cvode_mem, t_back, y)); - t = t_back; + int max_offset = num_reactions + num_species; + for (int mem_i = 0; mem_i < max_offset; ++mem_i) { + NV_Ith_S(y0, mem_i) = NV_Ith_S(y, mem_i); + } + + t0 = t; + return t0; +} + +double Integrator::restore_state() +{ + int max_offset = num_reactions + num_species; + for (int mem_i = 0; mem_i < max_offset; ++mem_i) { + NV_Ith_S(y, mem_i) = NV_Ith_S(y0, mem_i); + } + validate(CVodeReInit(cvode_mem, t0, y0)); + + return t0; +} + +void Integrator::refresh_state() +{ + validate(CVodeReInit(cvode_mem, t, y)); } Integrator::~Integrator() diff --git a/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/integrator.h b/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/integrator.h index 732ea9322..ec8f30340 100644 --- a/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/integrator.h +++ b/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/integrator.h @@ -48,13 +48,36 @@ namespace Gillespy::TauHybrid private: void *cvode_mem; N_Vector y0; + double t0; SUNLinearSolver solver; int num_species; int num_reactions; public: N_Vector y; realtype t; - void reset(double t_back); + + /* save_state() + * Creates a duplicate copy of the integrator's current solution vector. + * Contents of the most recent duplicate will be restored when restore_state() is called. + * + * Returns the time value of the integrator's saved state. + */ + double save_state(); + + /* restore_state() + * Loads the most recent duplicated copy of the solution vector. + * + * Returns the time value that the integrator was restored to. + */ + double restore_state(); + + /* refresh_state() + * Loads any new changes to the solution vector without changing previous output. + * Any new values assigned to the public N_Vector y will be recognized by the integrator. + * The current time value remains the same. To change this, modify `t`. + */ + void refresh_state(); + IntegrationResults integrate(double *t); IntegratorData data; diff --git a/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/rhs.cpp b/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/rhs.cpp index 4be8b783d..558099cd5 100644 --- a/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/rhs.cpp +++ b/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/rhs.cpp @@ -67,7 +67,8 @@ int Gillespy::TauHybrid::rhs(realtype t, N_Vector y, N_Vector ydot, void *user_d // Process stochastic reaction state by updating the root offset for each reaction. propensity = sim->propensity_function->TauEvaluate(rxn_i, populations); dydt_offsets[rxn_i] += propensity; - // break; left out on purpose, continuous results happen no matter what + break; + case SimulationState::CONTINUOUS: propensity = sim->propensity_function->ODEEvaluate(rxn_i, concentrations); propensities[rxn_i] = propensity; From 1da3d5afa6be7e1e0adaf504cebbeb60026b87a5 Mon Sep 17 00:00:00 2001 From: Josh C Date: Wed, 23 Jun 2021 10:05:50 -0400 Subject: [PATCH 67/88] Update hybrid solver to reflect build changes - Use `Simulation` and templated functions - Update main method to use arg parser - Add delegating constructor to hybrid sim to prevent resource waste --- gillespy2/solvers/cpp/c_base/Makefile | 18 +++---- .../tau_hybrid_cpp_solver/HybridModel.cpp | 10 ++-- .../tau_hybrid_cpp_solver/HybridModel.h | 4 +- .../TauHybridSimulation.cpp | 47 ++++--------------- .../tau_hybrid_cpp_solver/TauHybridSolver.cpp | 4 +- 5 files changed, 26 insertions(+), 57 deletions(-) diff --git a/gillespy2/solvers/cpp/c_base/Makefile b/gillespy2/solvers/cpp/c_base/Makefile index af5ad7d2f..632b0100d 100644 --- a/gillespy2/solvers/cpp/c_base/Makefile +++ b/gillespy2/solvers/cpp/c_base/Makefile @@ -23,7 +23,7 @@ OBJ_DIR ?= $(CBASE_DIR) OUTPUT_DIR ?= $(CBASE_DIR) OUTPUT_FILE ?= $(OUTPUT_DIR)/Simulation.out EXE_CMD ?= ./ -EXE_ARGS ?= -trajectories 1 -end 10 -timesteps 11 -increment 1 +EXE_ARGS ?= --trajectories 1 --end 10 --timesteps 11 --increment 1 SUNDIALS_OBJ ?= $(OBJ_DIR) INCLUDES := -I$(CBASE_DIR) -I$(SUNDIALS_INC) -I$(TEMPLATE_DIR) -I$(TAU_DIR) ########################## @@ -50,19 +50,19 @@ $(OBJ_DIR)/arg_parser.o: $(CBASE_DIR)/arg_parser.cpp $(CBASE_DIR)/arg_parser.h arg_parser: $(OBJ_DIR)/arg_parser.o ; $(SUNOBJ_PATHS): $(SUNDIALS_OBJ)/%.o: $(SUNDIALS_SRC)/%.c - $(CC) -c -o $@ $< $(CXXFLAGS) -I$(SUNDIALS_INC) + $(CXX) -c -o $@ $< $(CXXFLAGS) -I$(SUNDIALS_INC) sundials: $(SUNOBJ_PATHS); $(TAU_DEPS_PATHS): $(OBJ_DIR)/%.o: $(TAU_DIR)/%.cpp - $(CC) -c -o $@ $< $(CXXFLAGS) $(INCLUDES) + $(CXX) -c -o $@ $< $(CXXFLAGS) $(INCLUDES) tau_deps: $(TAU_DEPS_PATHS); $(TAU_HYBRID_DEPS_PATHS): $(OBJ_DIR)/%.o: $(TAU_HYBRID_SOLVER_PATH)/%.cpp - $(CC) -c -o $@ $< $(CXXFLAGS) $(INCLUDES) + $(CXX) -c -o $@ $< $(CXXFLAGS) $(INCLUDES) hybrid_deps: $(TAU_HYBRID_DEPS_PATHS); Tau.o: $(TAU_DIR)/tau.cpp $(TAU_DIR)/tau.h - $(CC) $(CXXFLAGS) -c -o Tau.o $(TAU_DIR)/tau.cpp $(INCLUDES) + $(CXX) $(CXXFLAGS) -c -o Tau.o $(TAU_DIR)/tau.cpp $(INCLUDES) ################################# ### SOLVER ALGORITHM COMPILE #### @@ -76,7 +76,7 @@ $(OBJ_DIR)/TauLeapingSolver.o: $(TAU_LEAPING_SOLVER_PATH)/TauLeapingSolver.cpp $(CXX) $(CXXFLAGS) -c -o $(OBJ_DIR)/TauLeapingSolver.o $(TAU_LEAPING_SOLVER_PATH)/TauLeapingSolver.cpp $(INCLUDES) $(OBJ_DIR)/TauHybridSolver.o: $(TAU_HYBRID_SOLVER_PATH)/TauHybridSolver.cpp - $(CC) $(CXXFLAGS) -c -o $(OBJ_DIR)/TauHybridSolver.o $(TAU_HYBRID_SOLVER_PATH)/TauHybridSolver.cpp $(INCLUDES) + $(CXX) $(CXXFLAGS) -c -o $(OBJ_DIR)/TauHybridSolver.o $(TAU_HYBRID_SOLVER_PATH)/TauHybridSolver.cpp $(INCLUDES) %Solver.o: $(OBJ_DIR)/%Solver.o ; @@ -93,7 +93,7 @@ $(OBJ_DIR)/TauLeapingSimulation.o: $(TAU_LEAPING_SOLVER_PATH)/TauLeapingSimulati $(CXX) $(CXXFLAGS) -c -o $(OBJ_DIR)/TauLeapingSimulation.o $(TAU_LEAPING_SOLVER_PATH)/TauLeapingSimulation.cpp $(INCLUDES) $(OBJ_DIR)/TauHybridSimulation.o: $(TAU_HYBRID_SOLVER_PATH)/TauHybridSimulation.cpp - $(CC) $(CXXFLAGS) -c -o $(OBJ_DIR)/TauHybridSimulation.o $(TAU_HYBRID_SOLVER_PATH)/TauHybridSimulation.cpp $(INCLUDES) + $(CXX) $(CXXFLAGS) -c -o $(OBJ_DIR)/TauHybridSimulation.o $(TAU_HYBRID_SOLVER_PATH)/TauHybridSimulation.cpp $(INCLUDES) %Simulation.o: $(OBJ_DIR)/%Simulation.o ; @@ -120,8 +120,8 @@ ssa: SSASimulation.o SSASolver.o model arg_parser $(TEMPLATE_CPP) tau_leap: TauLeapingSimulation.o TauLeapingSolver.o model arg_parser $(TEMPLATE_CPP) $(CXX) $(COMPILATION_ARGS) $(OBJ_DIR)/TauLeapingSimulation.o $(OBJ_DIR)/TauLeapingSolver.o -hybrid: TauHybridSimulation.o TauHybridSolver.o model sundials tau_deps hybrid_deps $(TEMPLATE_CPP) - $(CC) $(COMPILATION_ARGS) $(SUNOBJ_PATHS) $(TAU_HYBRID_DEPS_PATHS) $(TAU_DEPS_PATHS) $(OBJ_DIR)/TauHybridSimulation.o $(OBJ_DIR)/TauHybridSolver.o +hybrid: TauHybridSimulation.o TauHybridSolver.o model arg_parser sundials tau_deps hybrid_deps $(TEMPLATE_CPP) + $(CXX) $(COMPILATION_ARGS) $(SUNOBJ_PATHS) $(TAU_HYBRID_DEPS_PATHS) $(TAU_DEPS_PATHS) $(OBJ_DIR)/TauHybridSimulation.o $(OBJ_DIR)/TauHybridSolver.o build: $(SOLVER); diff --git a/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/HybridModel.cpp b/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/HybridModel.cpp index 25214b92f..c6c452b95 100644 --- a/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/HybridModel.cpp +++ b/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/HybridModel.cpp @@ -19,14 +19,10 @@ namespace Gillespy::TauHybrid // Empty constructor body } - HybridSimulation::~HybridSimulation() + HybridSimulation::HybridSimulation() + : Simulation() { - if (type == HYBRID) { - for(unsigned int i = 0; i < number_trajectories; i++){ - delete trajectoriesODE[i]; - } - delete trajectoriesODE; - } + // Empty constructor body } } diff --git a/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/HybridModel.h b/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/HybridModel.h index 28c1eba7a..773e0073e 100644 --- a/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/HybridModel.h +++ b/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/HybridModel.h @@ -58,14 +58,14 @@ namespace Gillespy::TauHybrid { double continuous; }; - struct HybridSimulation : Simulation + struct HybridSimulation : Simulation { hybrid_state *trajectories_hybrid1D; hybrid_state ***trajectories_hybrid; double hybrid_propensity(int reaction_id, std::vector ¤t_state); - ~HybridSimulation(); + HybridSimulation(); }; } diff --git a/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/TauHybridSimulation.cpp b/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/TauHybridSimulation.cpp index a68094740..b4903c496 100644 --- a/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/TauHybridSimulation.cpp +++ b/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/TauHybridSimulation.cpp @@ -6,6 +6,7 @@ #include #include "TauHybridSolver.h" #include "template.h" +#include "arg_parser.h" using namespace Gillespy; //Default values, replaced with command line args @@ -36,41 +37,14 @@ double Gillespy::TauHybrid::HybridSimulation::hybrid_propensity( } int main(int argc, char* argv[]){ - //Parse command line arguments - std :: string arg; - for(int i = 1; i < argc - 1; i++){ - arg = argv[i]; - if(argc > i+1 && arg.size() > 1 && arg[0] == '-'){ - std :: stringstream arg_stream(argv[i+1]); - switch(arg[1]){ - case 's': - arg_stream >> random_seed; - seed_time = false; - break; - case 'e': - arg_stream >> end_time; - break; - case 'i': - if (arg[2] == 'c') - arg_stream >> increment; - else if (arg[2] == 'i') - map_variable_populations(arg_stream); - break; - case 'p': - map_variable_parameters(arg_stream); - break; - case 't': - if(arg[2] == 'r'){ - arg_stream >> number_trajectories; - }else if(arg[2] == 'i'){ - arg_stream >> number_timesteps; - }else if (arg[2] == 'a'){ // '-tau_tol' - arg_stream >> tau_tol; - } - break; - } - } - } + ArgParser parser(argc, argv); + random_seed = parser.seed; + seed_time = (random_seed == -1); + + end_time = parser.end; + number_trajectories = parser.trajectories; + number_timesteps = parser.timesteps; + tau_tol = parser.tau_tol; Model model(species_names, species_populations, reaction_names); add_reactions(model); @@ -81,14 +55,13 @@ int main(int argc, char* argv[]){ IPropensityFunction *propFun = new PropensityFunction(); //Simulation INIT TauHybrid::HybridSimulation simulation; - simulation.type = HYBRID; simulation.model = &model; simulation.end_time = end_time; simulation.random_seed = random_seed; simulation.number_timesteps = number_timesteps; simulation.number_trajectories = number_trajectories; simulation.propensity_function = propFun; - simulationINIT(&model, simulation); + init_simulation(&model, simulation); // Perform ODE // TauHybrid::TauHybridCSolver(&simulation, tau_tol); simulation.output_results_buffer(std::cout); diff --git a/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/TauHybridSolver.cpp b/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/TauHybridSolver.cpp index bba4ef1c3..43f2f5bf8 100644 --- a/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/TauHybridSolver.cpp +++ b/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/TauHybridSolver.cpp @@ -40,7 +40,7 @@ namespace Gillespy::TauHybrid { //copy initial state for each trajectory for(int s = 0; s < num_species; s++){ - simulation->trajectoriesODE[0][0][s] = species[s].initial_population; + simulation->trajectories[0][0][s] = species[s].initial_population; } //Simulate for each trajectory @@ -169,7 +169,7 @@ namespace Gillespy::TauHybrid { while (save_time <= next_time) { // Write each species, one at a time (from ODE solution) for (int spec_i = 0; spec_i < num_species; ++spec_i) { - simulation->trajectoriesODE[traj][(int) save_time][spec_i] = current_state[spec_i]; + simulation->trajectories[traj][(int) save_time][spec_i] = current_state[spec_i]; } save_time += increment; } From f441c14f72eece1c4b66bb4be1c1d18953323125 Mon Sep 17 00:00:00 2001 From: Josh C Date: Wed, 23 Jun 2021 12:10:34 -0400 Subject: [PATCH 68/88] Updates to save time computations Previously, save times were "computed" as the floor of the `save_time` double. - Seeks along timeline to find next valid `save_time` on current timestep range (t0,t0+tau] - Save split `tau_step` when integration state failed --- gillespy2/solvers/cpp/c_base/Makefile | 2 +- .../tau_hybrid_cpp_solver/TauHybridSolver.cpp | 17 ++++++++++------- gillespy2/solvers/cpp/tau_hybrid_c_solver.py | 1 - 3 files changed, 11 insertions(+), 9 deletions(-) diff --git a/gillespy2/solvers/cpp/c_base/Makefile b/gillespy2/solvers/cpp/c_base/Makefile index 632b0100d..db40ba618 100644 --- a/gillespy2/solvers/cpp/c_base/Makefile +++ b/gillespy2/solvers/cpp/c_base/Makefile @@ -129,7 +129,7 @@ run: build ./$(OUTPUT_FILE) $(EXE_ARGS) debug: CXXFLAGS = -std=c++14 -Wall -g -debug: clean_all build; +debug: build; gdb --args $(OUTPUT_FILE) $(EXE_ARGS) all: prebuild diff --git a/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/TauHybridSolver.cpp b/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/TauHybridSolver.cpp index 43f2f5bf8..81de9e018 100644 --- a/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/TauHybridSolver.cpp +++ b/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/TauHybridSolver.cpp @@ -66,9 +66,10 @@ namespace Gillespy::TauHybrid { int rxn_offset_boundary = num_species + num_reactions; // SIMULATION STEP LOOP + int save_idx = 1; double next_time; double tau_step = 0.0; - double save_time = simulation->timeline[1]; + double save_time = simulation->timeline[save_idx]; // Temporary array to store changes to dependent species. // Should be 0-initialized each time it's used. @@ -150,7 +151,8 @@ namespace Gillespy::TauHybrid { // Only update state with the given population changes if valid. if (invalid_state) { sol.restore_state(); - next_time = simulation->current_time + (tau_step / 2); + tau_step /= 2; + next_time = simulation->current_time + tau_step; } else { // "Permanently" update the rxn_state and populations. @@ -165,13 +167,14 @@ namespace Gillespy::TauHybrid { // Output the results for this time step. sol.refresh_state(); simulation->current_time = next_time; - - while (save_time <= next_time) { - // Write each species, one at a time (from ODE solution) + + // Seek forward, writing out any values on the timeline which are on current timestep range. + // + while (save_idx < simulation->number_timesteps && save_time <= next_time) { for (int spec_i = 0; spec_i < num_species; ++spec_i) { - simulation->trajectories[traj][(int) save_time][spec_i] = current_state[spec_i]; + simulation->trajectories[traj][save_idx][spec_i] = current_state[spec_i]; } - save_time += increment; + save_time = simulation->timeline[++save_idx]; } } diff --git a/gillespy2/solvers/cpp/tau_hybrid_c_solver.py b/gillespy2/solvers/cpp/tau_hybrid_c_solver.py index 51567e096..2edf794a0 100644 --- a/gillespy2/solvers/cpp/tau_hybrid_c_solver.py +++ b/gillespy2/solvers/cpp/tau_hybrid_c_solver.py @@ -41,7 +41,6 @@ def run(self=None, model: Model = None, t: int = 20, number_of_trajectories: int args = { "trajectories": number_of_trajectories, "timesteps": number_timesteps, - "tau_step": tau_step, "tau_tol": tau_tol, "end": t } From 937a5cd324c232abe50a783d75930ecf6d86777d Mon Sep 17 00:00:00 2001 From: Josh C Date: Wed, 23 Jun 2021 18:13:25 -0400 Subject: [PATCH 69/88] Transition to differential equations for deterministic RHS - New class and functions for differential equations - Evaluate each species' diff eqs as a sequence of propensity evaluations - Remove `CONTINUOUS` block from RHS - Use `std::vector` for hybrid state instead of array --- .../tau_hybrid_cpp_solver/HybridModel.cpp | 75 ++++++++++++++++++- .../tau_hybrid_cpp_solver/HybridModel.h | 27 +++++++ .../TauHybridSimulation.cpp | 14 ++++ .../tau_hybrid_cpp_solver/integrator.cpp | 13 +--- .../c_base/tau_hybrid_cpp_solver/integrator.h | 5 +- .../cpp/c_base/tau_hybrid_cpp_solver/rhs.cpp | 27 +++---- 6 files changed, 128 insertions(+), 33 deletions(-) diff --git a/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/HybridModel.cpp b/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/HybridModel.cpp index c6c452b95..6e74774c9 100644 --- a/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/HybridModel.cpp +++ b/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/HybridModel.cpp @@ -4,15 +4,15 @@ namespace Gillespy::TauHybrid { HybridReaction::HybridReaction() - : mode(SimulationState::DISCRETE), + : mode(SimulationState::CONTINUOUS), base_reaction(nullptr) { // Empty constructor body } HybridSpecies::HybridSpecies() - : user_mode(SimulationState::DISCRETE), - partition_mode(SimulationState::DISCRETE), + : user_mode(SimulationState::CONTINUOUS), + partition_mode(SimulationState::CONTINUOUS), switch_tol(0.03), switch_min(0) { @@ -25,4 +25,73 @@ namespace Gillespy::TauHybrid // Empty constructor body } + + + double DifferentialEquation::evaluate( + std::vector &ode_state, + std::vector &ssa_state) + { + double sum = 0.0; + + for (auto &formula : formulas) { + sum += formula(ode_state, ssa_state); + } + + return sum; + } + + + void create_differential_equations( + std::vector &species, + std::vector &reactions) + { + // For now, differential equations are generated from scratch. + // It may be more efficient to determine which formulas need to change. + // Until then, the compound formulas in every species are cleared. + for (HybridSpecies spec : species) { + spec.diff_equation.formulas.clear(); + } + + for (int rxn_i = 0; rxn_i < reactions.size(); ++rxn_i) { + HybridReaction rxn = reactions[rxn_i]; + if (rxn.mode == SimulationState::DISCRETE) { + continue; + } + + for (int spec_i = 0; spec_i < species.size(); ++spec_i) { + // A species change of 0 indicates that this species is not a dependency for this reaction. + if (rxn.base_reaction->species_change[spec_i] == 0) { + continue; + } + + HybridSpecies &spec = species[spec_i]; + auto &formula_set = spec.diff_equation.formulas; + int spec_diff = rxn.base_reaction->species_change[spec_i]; + + switch (spec.partition_mode) { + case SimulationState::CONTINUOUS: + formula_set.push_back([&rxn, rxn_i, spec_diff]( + std::vector &ode_state, + std::vector &ssa_state) + { + return spec_diff * rxn.ode_propensity(rxn_i, ode_state); + }); + break; + + case SimulationState::DISCRETE: + spec.diff_equation.formulas.push_back([&rxn, rxn_i, spec_diff]( + std::vector &ode_state, + std::vector &ssa_state) + { + return spec_diff * rxn.ssa_propensity(rxn_i, ssa_state); + }); + break; + + default: + break; + } + } + } + } + } diff --git a/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/HybridModel.h b/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/HybridModel.h index 773e0073e..a852d17ff 100644 --- a/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/HybridModel.h +++ b/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/HybridModel.h @@ -1,6 +1,7 @@ #pragma once #include +#include #include "model.h" #define GPY_HYBRID_ABSTOL 1e-5 @@ -8,6 +9,22 @@ namespace Gillespy::TauHybrid { + typedef int ReactionId; + + /* Gillespy::TauHybrid::DiffEquation + * A vector containing evaluable functions, which accept integrator state and return propensities. + * + * The vector is understood to be an arbitrarily sized collection of propensity evaluations, + * each weighted by some individual, constant factor. + * The sum of evaulations of all collected functions is interpreted to be the dydt of that state. + */ + struct DifferentialEquation + { + public: + std::vector&, std::vector&)>> formulas; + double evaluate(std::vector &ode_state, std::vector &ssa_state); + }; + enum SimulationState { CONTINUOUS = 0, @@ -41,6 +58,8 @@ namespace Gillespy::TauHybrid { // If a value is given, switch_min will be used instead of switch_tol. unsigned int switch_min = 0; + DifferentialEquation diff_equation; + HybridSpecies(); }; @@ -50,6 +69,9 @@ namespace Gillespy::TauHybrid { SimulationState mode : 1; HybridReaction(); + + double ode_propensity(ReactionId reaction_number, std::vector &state); + double ssa_propensity(ReactionId reaction_number, std::vector &state); }; union hybrid_state @@ -68,4 +90,9 @@ namespace Gillespy::TauHybrid { HybridSimulation(); }; + + void create_differential_equations( + std::vector &species, + std::vector &reactions); + } diff --git a/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/TauHybridSimulation.cpp b/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/TauHybridSimulation.cpp index b4903c496..8072d3621 100644 --- a/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/TauHybridSimulation.cpp +++ b/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/TauHybridSimulation.cpp @@ -36,6 +36,20 @@ double Gillespy::TauHybrid::HybridSimulation::hybrid_propensity( return map_ode_propensity(reaction_id, S); } +double Gillespy::TauHybrid::HybridReaction::ode_propensity( + ReactionId reaction_number, + std::vector &state) +{ + return map_ode_propensity(reaction_number, state); +} + +double Gillespy::TauHybrid::HybridReaction::ssa_propensity( + ReactionId reaction_number, + std::vector &state) +{ + return map_propensity(reaction_number, state); +} + int main(int argc, char* argv[]){ ArgParser parser(argc, argv); random_seed = parser.seed; diff --git a/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/integrator.cpp b/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/integrator.cpp index 0dc76d138..c1d57fb52 100644 --- a/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/integrator.cpp +++ b/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/integrator.cpp @@ -13,14 +13,14 @@ IntegratorData::IntegratorData( : simulation(simulation), concentrations(std::vector(num_species)), populations(std::vector(num_species)), - propensities(std::vector(num_reactions)) + propensities(std::vector(num_reactions)), + species_state(num_species), + reaction_state(num_reactions) { - species_state = new HybridSpecies[num_species]; for (int spec_i = 0; spec_i < num_species; ++spec_i) { species_state[spec_i].base_species = &simulation->model->species[spec_i]; } - reaction_state = new HybridReaction[num_reactions]; for (int rxn_i = 0; rxn_i < num_reactions; ++rxn_i) { reaction_state[rxn_i].base_reaction = &simulation->model->reactions[rxn_i]; } @@ -32,12 +32,6 @@ IntegratorData::IntegratorData(HybridSimulation *simulation) simulation->model->number_species, simulation->model->number_reactions) {} -IntegratorData::~IntegratorData() -{ - delete[] species_state; - delete[] reaction_state; -} - Integrator::Integrator(HybridSimulation *simulation, N_Vector y0, double reltol, double abstol) : y0(y0), @@ -103,6 +97,7 @@ Integrator::~Integrator() IntegrationResults Integrator::integrate(double *t) { + create_differential_equations(data.species_state, data.reaction_state); if (!validate(CVode(cvode_mem, *t, y, &this->t, CV_NORMAL))) { return { nullptr, nullptr }; } diff --git a/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/integrator.h b/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/integrator.h index ec8f30340..1bf94a13f 100644 --- a/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/integrator.h +++ b/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/integrator.h @@ -14,8 +14,8 @@ namespace Gillespy::TauHybrid struct IntegratorData { HybridSimulation *simulation; - HybridSpecies *species_state; - HybridReaction *reaction_state; + std::vector species_state; + std::vector reaction_state; std::vector concentrations; std::vector populations; @@ -24,7 +24,6 @@ namespace Gillespy::TauHybrid IntegratorData(HybridSimulation *simulation); IntegratorData(HybridSimulation *simulation, int num_species, int num_reactions); IntegratorData(IntegratorData &prev_data); - ~IntegratorData(); }; /* :IntegrationResults: diff --git a/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/rhs.cpp b/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/rhs.cpp index 558099cd5..e31a2595c 100644 --- a/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/rhs.cpp +++ b/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/rhs.cpp @@ -18,8 +18,8 @@ int Gillespy::TauHybrid::rhs(realtype t, N_Vector y, N_Vector ydot, void *user_d // Extract simulation data IntegratorData *data = static_cast(user_data); HybridSimulation *sim = data->simulation; - HybridSpecies *species = data->species_state; - HybridReaction *reactions = data->reaction_state; + std::vector &species = data->species_state; + std::vector &reactions = data->reaction_state; std::vector &propensities = data->propensities; // Concentrations and reactions are both used for their respective propensity evaulations. // They both should, roughly, reflect the same data, but tau selection requires both. @@ -55,6 +55,13 @@ int Gillespy::TauHybrid::rhs(realtype t, N_Vector y, N_Vector ydot, void *user_d int species_change; Gillespy::Reaction *current_rxn; + // Deterministic reactions generally are "evaluated" by generating dy/dt functions + // for each of their dependent species. + // To handle these, we will go ahead and evaluate each species' differential equations. + for (spec_i = 0; spec_i < num_species; ++spec_i) { + dydt[spec_i] += species[spec_i].diff_equation.evaluate(concentrations, populations); + } + // Process deterministic propensity state // These updates get written directly to the integrator's concentration state for (rxn_i = 0; rxn_i < num_reactions; ++rxn_i) { @@ -70,22 +77,6 @@ int Gillespy::TauHybrid::rhs(realtype t, N_Vector y, N_Vector ydot, void *user_d break; case SimulationState::CONTINUOUS: - propensity = sim->propensity_function->ODEEvaluate(rxn_i, concentrations); - propensities[rxn_i] = propensity; - - for (spec_i = 0; spec_i < num_species; ++spec_i) { - // Use the evaluated propensity to update the concentration levels and reaction state. - // Propensity is treated as positive if it's a product, negative if it's a reactant. - species_change = current_rxn->species_change[spec_i]; - if (species_change == 0) - continue; - - // The product on the right evaluates to 1 if species_change is positive, - // and -1 if it's negative. - // This is a branchless alternative to using an if-statement. - dydt[spec_i] += propensity * (-1 + 2 * (species_change > 0)); - } - break; default: break; } From 5962f2888c24d35548d5c098a21fb4c5a3e2c7f9 Mon Sep 17 00:00:00 2001 From: Joshua Cooper Date: Wed, 23 Jun 2021 20:53:38 -0400 Subject: [PATCH 70/88] Differential equations clear fix Formula diff eqs weren't being iterated over by reference, so "clearing" them did nothing. Fixed by iterating by reference. - Add `&` to iterator in `create_differential_equations` - Use `HybridReaction` propensities for discrete reactions --- .../cpp/c_base/tau_hybrid_cpp_solver/HybridModel.cpp | 2 +- gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/rhs.cpp | 6 ++++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/HybridModel.cpp b/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/HybridModel.cpp index 6e74774c9..cb0fc5d17 100644 --- a/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/HybridModel.cpp +++ b/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/HybridModel.cpp @@ -48,7 +48,7 @@ namespace Gillespy::TauHybrid // For now, differential equations are generated from scratch. // It may be more efficient to determine which formulas need to change. // Until then, the compound formulas in every species are cleared. - for (HybridSpecies spec : species) { + for (HybridSpecies &spec : species) { spec.diff_equation.formulas.clear(); } diff --git a/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/rhs.cpp b/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/rhs.cpp index e31a2595c..08ba858e1 100644 --- a/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/rhs.cpp +++ b/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/rhs.cpp @@ -59,7 +59,8 @@ int Gillespy::TauHybrid::rhs(realtype t, N_Vector y, N_Vector ydot, void *user_d // for each of their dependent species. // To handle these, we will go ahead and evaluate each species' differential equations. for (spec_i = 0; spec_i < num_species; ++spec_i) { - dydt[spec_i] += species[spec_i].diff_equation.evaluate(concentrations, populations); + if (species[spec_i].partition_mode == SimulationState::CONTINUOUS) + dydt[spec_i] = species[spec_i].diff_equation.evaluate(concentrations, populations); } // Process deterministic propensity state @@ -72,12 +73,13 @@ int Gillespy::TauHybrid::rhs(realtype t, N_Vector y, N_Vector ydot, void *user_d switch (reactions[rxn_i].mode) { case SimulationState::DISCRETE: // Process stochastic reaction state by updating the root offset for each reaction. - propensity = sim->propensity_function->TauEvaluate(rxn_i, populations); + propensity = reactions[rxn_i].ssa_propensity(rxn_i, populations); dydt_offsets[rxn_i] += propensity; break; case SimulationState::CONTINUOUS: default: + dydt_offsets[rxn_i] = 0; break; } } From 504c5683d9836bd0a06dbe75470f1adbac82bedc Mon Sep 17 00:00:00 2001 From: Joshua Cooper Date: Wed, 23 Jun 2021 21:41:52 -0400 Subject: [PATCH 71/88] Update propensity evaluation for Tau select - Populate `propensities` vector in RHS equation - May have to swap this out for `GetDky` call? --- .../cpp/c_base/tau_hybrid_cpp_solver/rhs.cpp | 25 +++---------------- 1 file changed, 4 insertions(+), 21 deletions(-) diff --git a/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/rhs.cpp b/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/rhs.cpp index 08ba858e1..81879da81 100644 --- a/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/rhs.cpp +++ b/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/rhs.cpp @@ -40,41 +40,24 @@ int Gillespy::TauHybrid::rhs(realtype t, N_Vector y, N_Vector ydot, void *user_d for (spec_i = 0; spec_i < num_species; ++spec_i) { concentrations[spec_i] = Y[spec_i]; populations[spec_i] = Y[spec_i]; - dydt[spec_i] = 0; } - // Populate the current stochastic state into the root offset vector. - // dy/dt results are initialized to zero, and become the change in offset. - unsigned int rxn_i; - for (rxn_i = 0; rxn_i < num_reactions; ++rxn_i) { - dydt_offsets[rxn_i] = 0; - } - - // Each species has a "spot" in the y and f(y,t) vector. - // For each species, place the result of f(y,t) into dydt vector. - int species_change; - Gillespy::Reaction *current_rxn; - // Deterministic reactions generally are "evaluated" by generating dy/dt functions // for each of their dependent species. // To handle these, we will go ahead and evaluate each species' differential equations. for (spec_i = 0; spec_i < num_species; ++spec_i) { - if (species[spec_i].partition_mode == SimulationState::CONTINUOUS) - dydt[spec_i] = species[spec_i].diff_equation.evaluate(concentrations, populations); + dydt[spec_i] = species[spec_i].diff_equation.evaluate(concentrations, populations); } // Process deterministic propensity state // These updates get written directly to the integrator's concentration state - for (rxn_i = 0; rxn_i < num_reactions; ++rxn_i) { - current_rxn = reactions[rxn_i].base_reaction; - // NOTE: we may need to evaluate ODE and Tau propensities separately. - // At the moment, it's unsure whether or not that's required. - + for (int rxn_i = 0; rxn_i < num_reactions; ++rxn_i) { switch (reactions[rxn_i].mode) { case SimulationState::DISCRETE: // Process stochastic reaction state by updating the root offset for each reaction. propensity = reactions[rxn_i].ssa_propensity(rxn_i, populations); - dydt_offsets[rxn_i] += propensity; + dydt_offsets[rxn_i] = propensity; + propensities[rxn_i] = propensity; break; case SimulationState::CONTINUOUS: From 5ce96f9b507cb305e0ef0c569cf2e920ae0fab83 Mon Sep 17 00:00:00 2001 From: Josh C Date: Thu, 24 Jun 2021 14:48:23 -0400 Subject: [PATCH 72/88] Basic layout for Hybrid-specific templating - Add optional template definitions to `template_defaults.h` - Add `template` directory to hybrid solver (will be copied later) --- .../template/hybrid_template.cpp | 17 +++++++++++++ .../template/hybrid_template.h | 14 +++++++++++ .../cpp/c_base/template/template_defaults.h | 25 +++++++++++++++++++ 3 files changed, 56 insertions(+) create mode 100644 gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/template/hybrid_template.cpp create mode 100644 gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/template/hybrid_template.h diff --git a/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/template/hybrid_template.cpp b/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/template/hybrid_template.cpp new file mode 100644 index 000000000..b77ced821 --- /dev/null +++ b/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/template/hybrid_template.cpp @@ -0,0 +1,17 @@ +#include "hybrid_template.h" + +namespace Gillespy::TauHybrid +{ + void map_species_modes(std::vector &species) + { + #define SPECIES_MODE(spec_id, spec_mode) species[spec_id].user_mode = spec_mode; + #define CONTINUOUS SimulationState::CONTINUOUS + #define DISCRETE SimulationState::DISCRETE + #define DYNAMIC SimulationState::DYNAMIC + GPY_HYBRID_SPECIES_MODES + #undef DYNAMIC + #undef DISCRETE + #undef CONTINUOUS + #undef SPECIES_MODE + } +} diff --git a/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/template/hybrid_template.h b/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/template/hybrid_template.h new file mode 100644 index 000000000..4d18f68e6 --- /dev/null +++ b/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/template/hybrid_template.h @@ -0,0 +1,14 @@ +#pragma once + +#define GPY_SOLVER_HYBRID + +#include "template.h" +#include "HybridModel.h" +#include "template_defaults.h" + +namespace Gillespy::TauHybrid +{ + + void map_species_modes(std::vector &species); + +} diff --git a/gillespy2/solvers/cpp/c_base/template/template_defaults.h b/gillespy2/solvers/cpp/c_base/template/template_defaults.h index bf94c2556..6fc80e5bf 100644 --- a/gillespy2/solvers/cpp/c_base/template/template_defaults.h +++ b/gillespy2/solvers/cpp/c_base/template/template_defaults.h @@ -39,3 +39,28 @@ #ifndef GPY_REACTION_NAMES #define GPY_REACTION_NAMES #endif + +// =============================================================== +// ================ HYBRID SOLVER OPTION DEFAULTS ================ +// =============================================================== + +/* GPY_HYBRID_SPECIES_MODES: Default, user-provided flags for how each species is to be represented. + * Populate each SPECIES_MODE() with two arguments: species ID and species mode. + * Possible values for species mode are: DISCRETE / CONTINUOUS / DYNAMIC + * + * + * #define GPY_HYBRID_SPECIES_MODES \ + * SPECIES_MODE(0, DISCRETE) \ + * SPECIES_MODE(1, CONTINUOUS) \ + * SPECIES_MODE(2, DYNAMIC) + */ +#ifdef GPY_SOLVER_HYBRID + +#ifndef GPY_HYBRID_SPECIES_MODES +#define GPY_HYBRID_SPECIES_MODES +#endif + +#endif + +// Import solver-specific template options. +#include "template_opts.h" From 1b42246ebd3f4eba8bccae42ac6938ee94a46a92 Mon Sep 17 00:00:00 2001 From: Josh C Date: Thu, 24 Jun 2021 17:04:25 -0400 Subject: [PATCH 73/88] Write Hybrid options to `template_opts.h` - Split `write_template` file write into a separate function, `write_definitions` - Use `write_definitions` to populate custom options into a header file - Add `prepare_options` method to Build Engine --- gillespy2/solvers/cpp/build/build_engine.py | 8 +++++++ gillespy2/solvers/cpp/build/template_gen.py | 7 ++++++ gillespy2/solvers/cpp/c_base/Makefile | 2 +- .../tau_hybrid_cpp_solver/TauHybridSolver.cpp | 4 ++++ .../{template => }/hybrid_template.cpp | 12 +++++----- .../{template => }/hybrid_template.h | 0 .../cpp/c_base/template/template_defaults.h | 6 ++--- .../cpp/c_base/template/template_opts.h | 9 +++++++ gillespy2/solvers/cpp/tau_hybrid_c_solver.py | 24 +++++++++++++++++++ 9 files changed, 62 insertions(+), 10 deletions(-) rename gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/{template => }/hybrid_template.cpp (53%) rename gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/{template => }/hybrid_template.h (100%) create mode 100644 gillespy2/solvers/cpp/c_base/template/template_opts.h diff --git a/gillespy2/solvers/cpp/build/build_engine.py b/gillespy2/solvers/cpp/build/build_engine.py index 1e30d53f7..db6a4ea85 100644 --- a/gillespy2/solvers/cpp/build/build_engine.py +++ b/gillespy2/solvers/cpp/build/build_engine.py @@ -11,6 +11,7 @@ class BuildEngine(): template_definitions_name = "template_definitions.h" + template_options_name = "template_opts.h" def __init__(self, debug: bool = False, output_dir: str = None): self.self_dir = Path(__file__).parent @@ -97,6 +98,13 @@ def prepare(self, model: Model, variable=False) -> str: return self.output_dir + def prepare_options(self, custom_definitions: "dict[str, str]"): + """ + """ + template_dir = self.output_dir.joinpath("template") + template_opts_path = template_dir.joinpath(self.template_options_name) + template_gen.write_definitions(str(template_opts_path), custom_definitions) + def build_cache(self, cache_dir: str, force_rebuild: bool = False): """ Build object dependencies and cache into directory for later use. diff --git a/gillespy2/solvers/cpp/build/template_gen.py b/gillespy2/solvers/cpp/build/template_gen.py index 1bdca9d7c..ae634c863 100644 --- a/gillespy2/solvers/cpp/build/template_gen.py +++ b/gillespy2/solvers/cpp/build/template_gen.py @@ -16,6 +16,13 @@ def write_template(path: str, model: Model, variable=False): # Get a dictionary of model defines and transform into a list of strings in # `#define KEY VALUE` format. defines = get_model_defines(model, variable) + write_definitions(path, defines) + +def write_definitions(path: str, defines: "dict[str, str]"): + """ + """ + # Definition dict is transformed into a list of C++ macro definitions, with: + # `#define KEY VALUE` format. template_lines = [(f"#define {key} {value}\n") for key, value in defines.items()] # Write generated lines to the template file. diff --git a/gillespy2/solvers/cpp/c_base/Makefile b/gillespy2/solvers/cpp/c_base/Makefile index db40ba618..dde621b38 100644 --- a/gillespy2/solvers/cpp/c_base/Makefile +++ b/gillespy2/solvers/cpp/c_base/Makefile @@ -36,7 +36,7 @@ SUNOBJ_PATHS := $(SUNOBJ:%.o=$(SUNDIALS_OBJ)/%.o) TAU_DEPENDENCIES = tau.o TAU_DEPS_PATHS := $(TAU_DEPENDENCIES:%.o=$(OBJ_DIR)/%.o) -TAU_HYBRID_DEPENDENCIES = HybridModel.o rhs.o integrator.o +TAU_HYBRID_DEPENDENCIES = HybridModel.o rhs.o integrator.o hybrid_template.o TAU_HYBRID_DEPS_PATHS := $(TAU_HYBRID_DEPENDENCIES:%.o=$(OBJ_DIR)/%.o) ################################### diff --git a/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/TauHybridSolver.cpp b/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/TauHybridSolver.cpp index 81de9e018..f17e4716c 100644 --- a/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/TauHybridSolver.cpp +++ b/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/TauHybridSolver.cpp @@ -10,6 +10,7 @@ #include "TauHybridSolver.h" #include "HybridModel.h" #include "integrator.h" +#include "hybrid_template.h" // #include "statistics.h" #include "tau.h" using namespace Gillespy; @@ -56,6 +57,9 @@ namespace Gillespy::TauHybrid { N_Vector y0 = init_model_vector(model, urn); Integrator sol(simulation, y0, GPY_HYBRID_RELTOL, GPY_HYBRID_ABSTOL); + // Inject user-defined species modes into the solver. + map_species_modes(sol.data.species_state); + // Initialize the species population for the trajectory. for (int spec_i = 0; spec_i < num_species; ++spec_i) { current_state[spec_i] = species[spec_i].initial_population; diff --git a/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/template/hybrid_template.cpp b/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/hybrid_template.cpp similarity index 53% rename from gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/template/hybrid_template.cpp rename to gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/hybrid_template.cpp index b77ced821..b936e97d7 100644 --- a/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/template/hybrid_template.cpp +++ b/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/hybrid_template.cpp @@ -5,13 +5,13 @@ namespace Gillespy::TauHybrid void map_species_modes(std::vector &species) { #define SPECIES_MODE(spec_id, spec_mode) species[spec_id].user_mode = spec_mode; - #define CONTINUOUS SimulationState::CONTINUOUS - #define DISCRETE SimulationState::DISCRETE - #define DYNAMIC SimulationState::DYNAMIC + #define CONTINUOUS_MODE SimulationState::CONTINUOUS + #define DISCRETE_MODE SimulationState::DISCRETE + #define DYNAMIC_MODE SimulationState::DYNAMIC GPY_HYBRID_SPECIES_MODES - #undef DYNAMIC - #undef DISCRETE - #undef CONTINUOUS + #undef DYNAMIC_MODE + #undef DISCRETE_MODE + #undef CONTINUOUS_MODE #undef SPECIES_MODE } } diff --git a/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/template/hybrid_template.h b/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/hybrid_template.h similarity index 100% rename from gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/template/hybrid_template.h rename to gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/hybrid_template.h diff --git a/gillespy2/solvers/cpp/c_base/template/template_defaults.h b/gillespy2/solvers/cpp/c_base/template/template_defaults.h index 6fc80e5bf..f1b49b7e4 100644 --- a/gillespy2/solvers/cpp/c_base/template/template_defaults.h +++ b/gillespy2/solvers/cpp/c_base/template/template_defaults.h @@ -50,9 +50,9 @@ * * * #define GPY_HYBRID_SPECIES_MODES \ - * SPECIES_MODE(0, DISCRETE) \ - * SPECIES_MODE(1, CONTINUOUS) \ - * SPECIES_MODE(2, DYNAMIC) + * SPECIES_MODE(0, DISCRETE_MODE) \ + * SPECIES_MODE(1, CONTINUOUS_MODE) \ + * SPECIES_MODE(2, DYNAMIC_MODE) */ #ifdef GPY_SOLVER_HYBRID diff --git a/gillespy2/solvers/cpp/c_base/template/template_opts.h b/gillespy2/solvers/cpp/c_base/template/template_opts.h new file mode 100644 index 000000000..2a34c7b35 --- /dev/null +++ b/gillespy2/solvers/cpp/c_base/template/template_opts.h @@ -0,0 +1,9 @@ +#pragma once + +/* template_opts.h + * Additional template header file containing solver-specific model options. + * Empty by default, intended to be written to by any solver to provide unique data. + * + * Default options are still provided in the template_defaults.h header file, + * providing solver-specific defaults with the GPY_SOLVER_* macro. + */ diff --git a/gillespy2/solvers/cpp/tau_hybrid_c_solver.py b/gillespy2/solvers/cpp/tau_hybrid_c_solver.py index 2edf794a0..597f7e4e6 100644 --- a/gillespy2/solvers/cpp/tau_hybrid_c_solver.py +++ b/gillespy2/solvers/cpp/tau_hybrid_c_solver.py @@ -1,9 +1,31 @@ +import gillespy2 from gillespy2.solvers.cpp.c_decoder import BasicSimDecoder from gillespy2.solvers.utilities import solverutils as cutils from gillespy2.core import GillesPySolver, gillespyError, Model from .c_solver import CSolver, SimulationReturnCode +def template_def_hybrid_species(species: "list[gillespy2.Species]"): + """ + Populates the given list of species modes into a set of template macro definitions. + Expected values of + """ + species_mode_map = { + "continuous": "CONTINUOUS_MODE", + "discrete": "DISCRETE_MODE", + "dynamic": "DYNAMIC_MODE", + } + + species_mode_list = [] + for spec_id, spec in enumerate(species): + # Continuous by default + mode_keyword = species_mode_map.get(spec.mode, species_mode_map["continuous"]) + species_mode_list.append(f"SPECIES_MODE({spec_id},{mode_keyword})") + + return { + f"GPY_HYBRID_SPECIES_MODES": " ".join(species_mode_list) + } + class TauHybridCSolver(GillesPySolver, CSolver): name = "TauHybridCSolver" target = "hybrid" @@ -65,6 +87,8 @@ def run(self=None, model: Model = None, t: int = 20, number_of_trajectories: int decoder = BasicSimDecoder.create_default(number_of_trajectories, number_timesteps, len(self.model.listOfSpecies)) sim_exec = self._build(model, self.target, self.variable, False) + hybrid_options = template_def_hybrid_species([model.listOfSpecies[spec] for spec in self.species]) + self.build_engine.prepare_options(hybrid_options) sim_status = self._run(sim_exec, args, decoder, timeout) if sim_status == SimulationReturnCode.FAILED: From ad38c5b46a74a5eed49f761a4f936626d1eedf95 Mon Sep 17 00:00:00 2001 From: Josh C Date: Fri, 25 Jun 2021 10:14:09 -0400 Subject: [PATCH 74/88] Working implementation of options injection - Added explicit constructor call in Tau Hybrid - Custom definitions accepted as parameter to `CSolver` class --- gillespy2/solvers/cpp/build/build_engine.py | 6 ++- gillespy2/solvers/cpp/c_solver.py | 11 +++-- gillespy2/solvers/cpp/tau_hybrid_c_solver.py | 52 +++++++++++--------- 3 files changed, 40 insertions(+), 29 deletions(-) diff --git a/gillespy2/solvers/cpp/build/build_engine.py b/gillespy2/solvers/cpp/build/build_engine.py index db6a4ea85..e5ad3c7a6 100644 --- a/gillespy2/solvers/cpp/build/build_engine.py +++ b/gillespy2/solvers/cpp/build/build_engine.py @@ -51,7 +51,7 @@ def get_missing_dependencies(cls) -> "list[str]": return missing - def prepare(self, model: Model, variable=False) -> str: + def prepare(self, model: Model, variable=False, cusotm_definitions: "dict[str, str]" = None) -> str: """ Prepare the template directory for compilation. The following operations will be performed: @@ -92,6 +92,10 @@ def prepare(self, model: Model, variable=False) -> str: template_file = self.template_dir.joinpath(self.template_definitions_name) template_file.unlink() template_gen.write_template(str(template_file), model, variable) + if cusotm_definitions is not None: + options_file = self.template_dir.joinpath(self.template_options_name) + options_file.unlink() + template_gen.write_definitions(str(options_file), cusotm_definitions) # With all required information gathered, create a Make instance. self.make = Make(str(self.makefile), str(self.output_dir), str(self.obj_dir)) diff --git a/gillespy2/solvers/cpp/c_solver.py b/gillespy2/solvers/cpp/c_solver.py index 7e5e21093..e6d3cf19a 100644 --- a/gillespy2/solvers/cpp/c_solver.py +++ b/gillespy2/solvers/cpp/c_solver.py @@ -21,12 +21,13 @@ class SimulationReturnCode(IntEnum): FAILED = -1 class CSolver: - def __init__(self, model: Model = None, output_directory: str = None, delete_directory: bool = True, resume=None, variable: bool = False): + def __init__(self, model: Model = None, output_directory: str = None, delete_directory: bool = True, resume=None, variable: bool = False, custom_definitions: "dict[str,str]" = None): self.delete_directory = False self.model = model self.resume = resume self.variable = variable - self.build_engine = None + self.build_engine: BuildEngine = None + self.custom_definitions = custom_definitions # Validate output_directory, ensure that it doesn't already exist if isinstance(output_directory, str): @@ -43,7 +44,7 @@ def __init__(self, model: Model = None, output_directory: str = None, delete_dir if self.model is None: return - self._build(model, self.target, variable, False) + self._build(model, self.target, variable, False, custom_definitions) self.species_mappings = self.model.sanitized_species_names() self.species = list(self.species_mappings.keys()) self.parameter_mappings = self.model.sanitized_parameter_names() @@ -60,7 +61,7 @@ def __del__(self): self.build_engine.clean() - def _build(self, model: Model, simulation_name: str, variable: bool, debug: bool = False) -> str: + def _build(self, model: Model, simulation_name: str, variable: bool, debug: bool = False, custom_definitions: "dict[str, str]" = None) -> str: """ Generate and build the simulation from the specified Model and solver_name into the output_dir. @@ -77,7 +78,7 @@ def _build(self, model: Model, simulation_name: str, variable: bool, debug: bool # Prepare the build workspace. if self.build_engine is None or self.build_engine.get_executable_path() is None: self.build_engine = BuildEngine(debug=debug, output_dir=self.output_directory) - self.build_engine.prepare(model, variable) + self.build_engine.prepare(model, variable, custom_definitions) # Compile the simulation, returning the path of the executable. return self.build_engine.build_simulation(simulation_name) diff --git a/gillespy2/solvers/cpp/tau_hybrid_c_solver.py b/gillespy2/solvers/cpp/tau_hybrid_c_solver.py index 597f7e4e6..2043ae20d 100644 --- a/gillespy2/solvers/cpp/tau_hybrid_c_solver.py +++ b/gillespy2/solvers/cpp/tau_hybrid_c_solver.py @@ -5,31 +5,39 @@ from .c_solver import CSolver, SimulationReturnCode -def template_def_hybrid_species(species: "list[gillespy2.Species]"): - """ - Populates the given list of species modes into a set of template macro definitions. - Expected values of - """ - species_mode_map = { - "continuous": "CONTINUOUS_MODE", - "discrete": "DISCRETE_MODE", - "dynamic": "DYNAMIC_MODE", - } - - species_mode_list = [] - for spec_id, spec in enumerate(species): - # Continuous by default - mode_keyword = species_mode_map.get(spec.mode, species_mode_map["continuous"]) - species_mode_list.append(f"SPECIES_MODE({spec_id},{mode_keyword})") - - return { - f"GPY_HYBRID_SPECIES_MODES": " ".join(species_mode_list) - } - class TauHybridCSolver(GillesPySolver, CSolver): name = "TauHybridCSolver" target = "hybrid" + def __init__(self, model: Model = None, output_directory: str = None, delete_directory: bool = True, resume=None, variable=False): + # if model is None: + # options = None + # else: + # options = TauHybridCSolver.__create_template_options(list(model.listOfSpecies.values())) + options = None if model is None else TauHybridCSolver.__create_template_options(list(model.listOfSpecies.values())) + super().__init__(model, output_directory, delete_directory, resume, variable, options) + + @classmethod + def __create_template_options(cls, species: "list[gillespy2.Species]"): + """ + Populate the given list of species modes into a set of template macro definitions. + """ + species_mode_map = { + "continuous": "CONTINUOUS_MODE", + "discrete": "DISCRETE_MODE", + "dynamic": "DYNAMIC_MODE", + } + + species_mode_list = [] + for spec_id, spec in enumerate(species): + # Continuous by default + mode_keyword = species_mode_map.get(spec.mode, species_mode_map["continuous"]) + species_mode_list.append(f"SPECIES_MODE({spec_id},{mode_keyword})") + + return { + f"GPY_HYBRID_SPECIES_MODES": " ".join(species_mode_list) + } + def get_solver_settings(self): """ :return: Tuple of strings, denoting all keyword argument for this solvers run() method. @@ -87,8 +95,6 @@ def run(self=None, model: Model = None, t: int = 20, number_of_trajectories: int decoder = BasicSimDecoder.create_default(number_of_trajectories, number_timesteps, len(self.model.listOfSpecies)) sim_exec = self._build(model, self.target, self.variable, False) - hybrid_options = template_def_hybrid_species([model.listOfSpecies[spec] for spec in self.species]) - self.build_engine.prepare_options(hybrid_options) sim_status = self._run(sim_exec, args, decoder, timeout) if sim_status == SimulationReturnCode.FAILED: From 47ca002cf7f212a4c9f63ac03d3999c514932b3b Mon Sep 17 00:00:00 2001 From: Josh C Date: Fri, 25 Jun 2021 11:05:01 -0400 Subject: [PATCH 75/88] Fix tests so GitHub will stop sending emails telling me my tests failed --- gillespy2/solvers/cpp/build/build_engine.py | 7 - gillespy2/solvers/cpp/c_base/Makefile | 9 +- gillespy2/solvers/cpp/c_base/Tau/tau.cpp | 417 ++++++++++-------- .../TauLeapingSolver.cpp | 373 +--------------- gillespy2/solvers/cpp/tau_hybrid_c_solver.py | 9 +- 5 files changed, 247 insertions(+), 568 deletions(-) diff --git a/gillespy2/solvers/cpp/build/build_engine.py b/gillespy2/solvers/cpp/build/build_engine.py index e5ad3c7a6..0a35f9d95 100644 --- a/gillespy2/solvers/cpp/build/build_engine.py +++ b/gillespy2/solvers/cpp/build/build_engine.py @@ -102,13 +102,6 @@ def prepare(self, model: Model, variable=False, cusotm_definitions: "dict[str, s return self.output_dir - def prepare_options(self, custom_definitions: "dict[str, str]"): - """ - """ - template_dir = self.output_dir.joinpath("template") - template_opts_path = template_dir.joinpath(self.template_options_name) - template_gen.write_definitions(str(template_opts_path), custom_definitions) - def build_cache(self, cache_dir: str, force_rebuild: bool = False): """ Build object dependencies and cache into directory for later use. diff --git a/gillespy2/solvers/cpp/c_base/Makefile b/gillespy2/solvers/cpp/c_base/Makefile index dde621b38..f3b5af80d 100644 --- a/gillespy2/solvers/cpp/c_base/Makefile +++ b/gillespy2/solvers/cpp/c_base/Makefile @@ -2,7 +2,7 @@ CXX := g++ CFLAGS := -Wall -O3 CXXFLAGS := -std=c++14 -Wall -O3 -GPY_SOLVER ?= ssa +SOLVER ?= ssa ####################################### ### Input directories ### @@ -61,9 +61,6 @@ $(TAU_HYBRID_DEPS_PATHS): $(OBJ_DIR)/%.o: $(TAU_HYBRID_SOLVER_PATH)/%.cpp $(CXX) -c -o $@ $< $(CXXFLAGS) $(INCLUDES) hybrid_deps: $(TAU_HYBRID_DEPS_PATHS); -Tau.o: $(TAU_DIR)/tau.cpp $(TAU_DIR)/tau.h - $(CXX) $(CXXFLAGS) -c -o Tau.o $(TAU_DIR)/tau.cpp $(INCLUDES) - ################################# ### SOLVER ALGORITHM COMPILE #### $(OBJ_DIR)/ODESolver.o: $(ODE_SOLVER_PATH)/ODESolver.cpp @@ -117,8 +114,8 @@ ode: ODESimulation.o ODESolver.o sundials model arg_parser $(TEMPLATE_CPP) ssa: SSASimulation.o SSASolver.o model arg_parser $(TEMPLATE_CPP) $(CXX) $(COMPILATION_ARGS) $(OBJ_DIR)/SSASimulation.o $(OBJ_DIR)/SSASolver.o -tau_leap: TauLeapingSimulation.o TauLeapingSolver.o model arg_parser $(TEMPLATE_CPP) - $(CXX) $(COMPILATION_ARGS) $(OBJ_DIR)/TauLeapingSimulation.o $(OBJ_DIR)/TauLeapingSolver.o +tau_leap: TauLeapingSimulation.o TauLeapingSolver.o tau_deps model arg_parser $(TEMPLATE_CPP) + $(CXX) $(COMPILATION_ARGS) $(OBJ_DIR)/TauLeapingSimulation.o $(OBJ_DIR)/tau.o $(OBJ_DIR)/TauLeapingSolver.o hybrid: TauHybridSimulation.o TauHybridSolver.o model arg_parser sundials tau_deps hybrid_deps $(TEMPLATE_CPP) $(CXX) $(COMPILATION_ARGS) $(SUNOBJ_PATHS) $(TAU_HYBRID_DEPS_PATHS) $(TAU_DEPS_PATHS) $(OBJ_DIR)/TauHybridSimulation.o $(OBJ_DIR)/TauHybridSolver.o diff --git a/gillespy2/solvers/cpp/c_base/Tau/tau.cpp b/gillespy2/solvers/cpp/c_base/Tau/tau.cpp index ad8b2281e..768eb4d1d 100644 --- a/gillespy2/solvers/cpp/c_base/Tau/tau.cpp +++ b/gillespy2/solvers/cpp/c_base/Tau/tau.cpp @@ -3,185 +3,238 @@ #include namespace Gillespy { - TauArgs initialize(Gillespy::Model &model, double tau_tol) - { - - // Initialize TauArgs struct to be returned as a pointer - TauArgs tau_args; - // Initialize highest order rxns to 0 - for (int i = 0; i < model.number_species; i++) - { - tau_args.HOR[model.species[i].name] = 0; - } - - for (int r = 0; r < model.number_reactions; r++) - { - int rxn_order = 0; - for (int spec = 0; spec < model.number_species; spec++) - { - if (model.reactions[r].species_change[spec] > 0) - tau_args.products[r].push_back(spec); - else if (model.reactions[r].species_change[spec] < 0) - { - rxn_order += 1; - tau_args.reactions_reactants[r].push_back(spec); - tau_args.reactants.insert(model.species[spec]); - } - } - - // if this reaction's order is higher than previous, set - if (tau_args.reactions_reactants[r].size() > 0) - { - for (auto const &reactant : tau_args.reactions_reactants[r]) - { - if (rxn_order > tau_args.HOR[model.species[reactant].name]) - { - tau_args.HOR[model.species[reactant].name] = rxn_order; - tau_args.g_i[model.species[reactant].name] = tau_args.HOR[model.species[reactant].name]; - - int count = std::abs(model.reactions[r].species_change[reactant]); - if (count == 2 && rxn_order == 2) - { - auto lambda = [](double x) { return (2 + (1 / (x - 1))); }; - tau_args.g_i_lambdas[model.species[reactant].name] = lambda; - } - else if (count == 2 && rxn_order == 3) - { - auto lambda = [](double x) { return ((3 / 2) * (2 + (1 / (x - 1)))); }; - tau_args.g_i_lambdas[model.species[reactant].name] = lambda; - } - else if (count == 3) - { - auto lambda = [](double x) { return (3 + (1 / (x - 1)) + (2 / (x - 2))); }; - tau_args.g_i_lambdas[model.species[reactant].name] = lambda; - } - else - { - tau_args.g_i[model.species[reactant].name] = tau_args.HOR[model.species[reactant].name]; - tau_args.epsilon_i[model.species[reactant].name] = tau_tol / tau_args.g_i[model.species[reactant].name]; - } - } - } - } - } - - return tau_args; - } - - double select(Gillespy::Model &model, TauArgs &tau_args, const double &tau_tol, const double ¤t_time, const double &save_time, const std::vector &propensity_values, const std::vector ¤t_state) - { - double tau; //tau time to step; - std::map critical_taus; //Mapping of possible critical_taus, to be evaluated - std::map mu_i; - std::map sigma_i; - bool critical = false; // system-wide flag, true when any reaction is critical - double non_critical_tau = 0; // holds the smallest tau time for non-critical reactions - double critical_tau = 0; // holds the smallest tau time for critical reactions - - int v; //used for number of population reactant consumes - - // initialize mu_i and sigma_i to 0 - for (int spec = 0; spec < model.number_species; spec++) - { - mu_i[model.species[spec].name] = 0; - sigma_i[model.species[spec].name] = 0; - } - // Determine if there are any critical reactions, update mu_i and sigma_i - for (int reaction = 0; reaction < model.number_reactions; reaction++) - { - for (auto const &reactant : tau_args.reactions_reactants[reaction]) - { - if (model.reactions[reaction].species_change[reactant] < 0) - { - v = abs(model.reactions[reaction].species_change[reactant]); - - if ((double)current_state[reactant] / v < tau_args.critical_threshold && propensity_values[reaction] > 0) - { - critical = true; // Critical reaction present in simulation - } - int consumed = abs(model.reactions[reaction].species_change[reactant]); - mu_i[model.species[reactant].name] += consumed * propensity_values[reaction]; //Cao, Gillespie, Petzold 32a - sigma_i[model.species[reactant].name] += std::pow(consumed, 2) * propensity_values[reaction]; //Cao, Gillespie, Petzold 32a - } - } - } - - // If a critical reaction is present, estimate tau for a single firing of each - // critical reaction with propensity > 0, and take the smallest tau - if (critical == true) - { - for (int reaction = 0; reaction < model.number_reactions; reaction++) - { - if (propensity_values[reaction] > 0) - critical_taus[model.reactions[reaction].name] = 1 / propensity_values[reaction]; - } - std::pair min; - //find min of critical_taus - min = *min_element(critical_taus.begin(), critical_taus.end(), [](const auto &lhs, const auto &rhs) { return lhs.second < rhs.second; }); - critical_tau = min.second; - } - - if (tau_args.g_i_lambdas.size() > 0) - { - for (auto const &x : tau_args.g_i_lambdas) - { - tau_args.g_i[x.first] = tau_args.g_i_lambdas[x.first](tau_args.g_i[x.first]); - tau_args.epsilon_i[x.first] = tau_tol / tau_args.g_i[x.first]; - tau_args.g_i_lambdas.erase(x.first); //MAYBE SHOULDN'T ERASE, may break loop/may be understanding this part wrong - } - } - - std::map tau_i; //Mapping of possible non-critical_taus, to be evaluated - - for (const auto &r : tau_args.reactants) - { - double calculated_max = tau_args.epsilon_i[r.name] * current_state[r.id]; - double max_pop_change_mean = std::max(calculated_max, 1.0); - double max_pop_change_sd = pow(max_pop_change_mean, 2); - if (mu_i[r.name] > 0) - { // Cao, Gillespie, Petzold 33 - tau_i[r.name] = std::min(std::abs(max_pop_change_mean / mu_i[r.name]), max_pop_change_sd / sigma_i[r.name]); - } - } - - if (tau_i.size() > 0) - { - std::pair min; - //find min of tau_i - min = *min_element(tau_i.begin(), tau_i.end(), [](const auto &lhs, const auto &rhs) { return lhs.second < rhs.second; }); - non_critical_tau = min.second; - } - - // If all reactions are non-critical, use non-critical tau. - if (critical == false) - { - tau = non_critical_tau; - } - // If all reactions are critical, use critical tau. - else if (tau_i.size() == 0) - { - tau = critical_tau; - } - // If there are both critical, and non critical reactions, - // Take the shortest tau between critica and non-critical. - else - { - tau = std::min(non_critical_tau, critical_tau); - } - // If selected tau exceeds save time, integrate to save time - if (tau > 0) - { - tau = std::max(tau, 1e-10); - if (save_time - current_time > 0) - { - tau = std::min(tau, save_time - current_time); - } - } - else - { - tau = save_time - current_time; - } - - return tau; - } -} \ No newline at end of file + + TauArgs initialize(Gillespy::Model &model, double tau_tol) + { + // Initialize TauArgs struct to be returned as a pointer + TauArgs tau_args; + + // Initialize highest order rxns to 0 + for (int i = 0; i < model.number_species; i++) + { + tau_args.HOR[model.species[i].name] = 0; + } + + for (int r = 0; r < model.number_reactions; r++) + { + int rxn_order = 0; + + for (int spec = 0; spec < model.number_species; spec++) + { + if (model.reactions[r].species_change[spec] > 0) + { + tau_args.products[r].push_back(spec); + } + + else if (model.reactions[r].species_change[spec] < 0) + { + rxn_order += 1; + tau_args.reactions_reactants[r].push_back(spec); + tau_args.reactants.insert(model.species[spec]); + } + } + + // if this reaction's order is higher than previous, set + if (tau_args.reactions_reactants[r].size() > 0) + { + for (auto const &reactant : tau_args.reactions_reactants[r]) + { + if (rxn_order <= tau_args.HOR[model.species[reactant].name]) + { + continue; + } + + tau_args.HOR[model.species[reactant].name] = rxn_order; + tau_args.g_i[model.species[reactant].name] = tau_args.HOR[model.species[reactant].name]; + + int count = std::abs(model.reactions[r].species_change[reactant]); + + if (count == 2 && rxn_order == 2) + { + auto lambda = [](double x) + { + return (2 + (1 / (x - 1))); + }; + tau_args.g_i_lambdas[model.species[reactant].name] = lambda; + } + + else if (count == 2 && rxn_order == 3) + { + auto lambda = [](double x) + { + return ((3 / 2) * (2 + (1 / (x - 1)))); + }; + tau_args.g_i_lambdas[model.species[reactant].name] = lambda; + } + + else if (count == 3) + { + auto lambda = [](double x) + { + return (3 + (1 / (x - 1)) + (2 / (x - 2))); + }; + tau_args.g_i_lambdas[model.species[reactant].name] = lambda; + } + + else + { + tau_args.g_i[model.species[reactant].name] = tau_args.HOR[model.species[reactant].name]; + tau_args.epsilon_i[model.species[reactant].name] = tau_tol / tau_args.g_i[model.species[reactant].name]; + } + } + } + } + + return tau_args; + } + + double select( + Gillespy::Model &model, + TauArgs &tau_args, + const double &tau_tol, + const double ¤t_time, + const double &save_time, + const std::vector &propensity_values, + const std::vector ¤t_state) + { + + bool critical = false; // system-wide flag, true when any reaction is critical + + int v;//used for number of population reactant consumes + double tau; //tau time to step; + double non_critical_tau = 0; // holds the smallest tau time for non-critical reactions + double critical_tau = 0; // holds the smallest tau time for critical reactions + + std::map critical_taus; //Mapping of possible critical_taus, to be evaluated + std::map mu_i; + std::map sigma_i; + + // initialize mu_i and sigma_i to 0 + for (int spec = 0; spec < model.number_species; spec++) + { + mu_i[model.species[spec].name] = 0; + sigma_i[model.species[spec].name] = 0; + } + + // Determine if there are any critical reactions, update mu_i and sigma_i + for (int reaction = 0; reaction < model.number_reactions; reaction++) + { + for (auto const &reactant : tau_args.reactions_reactants[reaction]) + { + if (model.reactions[reaction].species_change[reactant] >= 0) + { + continue; + } + + v = abs(model.reactions[reaction].species_change[reactant]); + + if ((double)current_state[reactant] / v < tau_args.critical_threshold && propensity_values[reaction] > 0) + { + critical = true; // Critical reaction present in simulation + } + + int consumed = abs(model.reactions[reaction].species_change[reactant]); + + mu_i[model.species[reactant].name] += consumed * propensity_values[reaction];//Cao, Gillespie, Petzold 32a + sigma_i[model.species[reactant].name] += std::pow(consumed, 2) * propensity_values[reaction];//Cao, Gillespie, Petzold 32a + } + } + + // If a critical reaction is present, estimate tau for a single firing of each + // critical reaction with propensity > 0, and take the smallest tau + if (critical == true) + { + for (int reaction = 0; reaction < model.number_reactions; reaction++) + { + if (propensity_values[reaction] > 0) + { + critical_taus[model.reactions[reaction].name] = 1 / propensity_values[reaction]; + } + } + + //find min of critical_taus + std::pair min; + min = *min_element(critical_taus.begin(), critical_taus.end(), [](const auto &lhs, const auto &rhs) + { + return lhs.second < rhs.second; + }); + + critical_tau = min.second; + } + + if (tau_args.g_i_lambdas.size() > 0) + { + for (auto const &x : tau_args.g_i_lambdas) + { + tau_args.g_i[x.first] = tau_args.g_i_lambdas[x.first](tau_args.g_i[x.first]); + + tau_args.epsilon_i[x.first] = tau_tol / tau_args.g_i[x.first]; + tau_args.g_i_lambdas.erase(x.first); + } + } + + std::map tau_i; //Mapping of possible non-critical_taus, to be evaluated + + for (const auto &r : tau_args.reactants) + { + double calculated_max = tau_args.epsilon_i[r.name] * current_state[r.id]; + double max_pop_change_mean = std::max(calculated_max, 1.0); + double max_pop_change_sd = pow(max_pop_change_mean, 2); + + // Cao, Gillespie, Petzold 33. + if (mu_i[r.name] > 0) + { + tau_i[r.name] = std::min(std::abs(max_pop_change_mean / mu_i[r.name]), max_pop_change_sd / sigma_i[r.name]); + } + } + + if (tau_i.size() > 0) + { + //find min of tau_i + std::pair min; + min = *min_element(tau_i.begin(), tau_i.end(), [](const auto &lhs, const auto &rhs) + { + return lhs.second < rhs.second; + }); + + non_critical_tau = min.second; + } + + // If all reactions are non-critical, use non-critical tau. + if (critical == false) + { + tau = non_critical_tau; + } + + // If all reactions are critical, use critical tau. + else if (tau_i.size() == 0) + { + tau = critical_tau; + } + + // If there are both critical, and non critical reactions, + // Take the shortest tau between critica and non-critical. + else + { + tau = std::min(non_critical_tau, critical_tau); + } + + // If selected tau exceeds save time, integrate to save time + if (tau > 0) + { + tau = std::max(tau, 1e-10); + + if (save_time - current_time > 0) + { + tau = std::min(tau, save_time - current_time); + } + } + + else + { + tau = save_time - current_time; + } + + return tau; + } +} diff --git a/gillespy2/solvers/cpp/c_base/tau_leaping_cpp_solver/TauLeapingSolver.cpp b/gillespy2/solvers/cpp/c_base/tau_leaping_cpp_solver/TauLeapingSolver.cpp index 969e1e387..b3b16ffe5 100644 --- a/gillespy2/solvers/cpp/c_base/tau_leaping_cpp_solver/TauLeapingSolver.cpp +++ b/gillespy2/solvers/cpp/c_base/tau_leaping_cpp_solver/TauLeapingSolver.cpp @@ -11,262 +11,16 @@ #include #include "TauLeapingSolver.h" - -static volatile bool interrupted = false; -static void signalHandler(int signum){ - interrupted = true; -} +#include "tau.h" namespace Gillespy { + bool interrupted = false; std::mt19937_64 generator; - struct TauArgs - { - int critical_threshold = 10; - - std::map HOR; - std::set reactants; - - std::map> g_i_lambdas; - std::map g_i; - std::map epsilon_i; - std::map> reactions_reactants; - std::map> products; - }; - - TauArgs initialize(Gillespy::Model &model, double tau_tol) + void signalHandler(int signum) { - // Initialize TauArgs struct to be returned as a pointer - TauArgs tau_args; - - // Initialize highest order rxns to 0 - for (int i = 0; i < model.number_species; i++) - { - tau_args.HOR[model.species[i].name] = 0; - } - - for (int r = 0; r < model.number_reactions; r++) - { - int rxn_order = 0; - - for (int spec = 0; spec < model.number_species; spec++) - { - if (model.reactions[r].species_change[spec] > 0) - { - tau_args.products[r].push_back(spec); - } - - else if (model.reactions[r].species_change[spec] < 0) - { - rxn_order += 1; - tau_args.reactions_reactants[r].push_back(spec); - tau_args.reactants.insert(model.species[spec]); - } - } - - // if this reaction's order is higher than previous, set - if (tau_args.reactions_reactants[r].size() > 0) - { - for (auto const &reactant : tau_args.reactions_reactants[r]) - { - if (rxn_order <= tau_args.HOR[model.species[reactant].name]) - { - continue; - } - - tau_args.HOR[model.species[reactant].name] = rxn_order; - tau_args.g_i[model.species[reactant].name] = tau_args.HOR[model.species[reactant].name]; - - int count = std::abs(model.reactions[r].species_change[reactant]); - - if (count == 2 && rxn_order == 2) - { - auto lambda = [](double x) - { - return (2 + (1 / (x - 1))); - }; - tau_args.g_i_lambdas[model.species[reactant].name] = lambda; - } - - else if (count == 2 && rxn_order == 3) - { - auto lambda = [](double x) - { - return ((3 / 2) * (2 + (1 / (x - 1)))); - }; - tau_args.g_i_lambdas[model.species[reactant].name] = lambda; - } - - else if (count == 3) - { - auto lambda = [](double x) - { - return (3 + (1 / (x - 1)) + (2 / (x - 2))); - }; - tau_args.g_i_lambdas[model.species[reactant].name] = lambda; - } - - else - { - tau_args.g_i[model.species[reactant].name] = tau_args.HOR[model.species[reactant].name]; - tau_args.epsilon_i[model.species[reactant].name] = tau_tol / tau_args.g_i[model.species[reactant].name]; - } - } - } - } - - return tau_args; - } - - double select( - Gillespy::Model &model, - TauArgs &tau_args, - const double &tau_tol, - const double ¤t_time, - const double &save_time, - const std::vector &propensity_values, - const std::vector ¤t_state) - { - - bool critical = false; // system-wide flag, true when any reaction is critical - - int v;//used for number of population reactant consumes - double tau; //tau time to step; - double non_critical_tau = 0; // holds the smallest tau time for non-critical reactions - double critical_tau = 0; // holds the smallest tau time for critical reactions - - std::map critical_taus; //Mapping of possible critical_taus, to be evaluated - std::map mu_i; - std::map sigma_i; - - // initialize mu_i and sigma_i to 0 - for (int spec = 0; spec < model.number_species; spec++) - { - mu_i[model.species[spec].name] = 0; - sigma_i[model.species[spec].name] = 0; - } - - // Determine if there are any critical reactions, update mu_i and sigma_i - for (int reaction = 0; reaction < model.number_reactions; reaction++) - { - for (auto const &reactant : tau_args.reactions_reactants[reaction]) - { - if (model.reactions[reaction].species_change[reactant] >= 0) - { - continue; - } - - v = abs(model.reactions[reaction].species_change[reactant]); - - if ((double)current_state[reactant] / v < tau_args.critical_threshold && propensity_values[reaction] > 0) - { - critical = true; // Critical reaction present in simulation - } - - int consumed = abs(model.reactions[reaction].species_change[reactant]); - - mu_i[model.species[reactant].name] += consumed * propensity_values[reaction];//Cao, Gillespie, Petzold 32a - sigma_i[model.species[reactant].name] += std::pow(consumed, 2) * propensity_values[reaction];//Cao, Gillespie, Petzold 32a - } - } - - // If a critical reaction is present, estimate tau for a single firing of each - // critical reaction with propensity > 0, and take the smallest tau - if (critical == true) - { - for (int reaction = 0; reaction < model.number_reactions; reaction++) - { - if (propensity_values[reaction] > 0) - { - critical_taus[model.reactions[reaction].name] = 1 / propensity_values[reaction]; - } - } - - //find min of critical_taus - std::pair min; - min = *min_element(critical_taus.begin(), critical_taus.end(), [](const auto &lhs, const auto &rhs) - { - return lhs.second < rhs.second; - }); - - critical_tau = min.second; - } - - if (tau_args.g_i_lambdas.size() > 0) - { - for (auto const &x : tau_args.g_i_lambdas) - { - tau_args.g_i[x.first] = tau_args.g_i_lambdas[x.first](tau_args.g_i[x.first]); - - tau_args.epsilon_i[x.first] = tau_tol / tau_args.g_i[x.first]; - tau_args.g_i_lambdas.erase(x.first); - } - } - - std::map tau_i; //Mapping of possible non-critical_taus, to be evaluated - - for (const auto &r : tau_args.reactants) - { - double calculated_max = tau_args.epsilon_i[r.name] * current_state[r.id]; - double max_pop_change_mean = std::max(calculated_max, 1.0); - double max_pop_change_sd = pow(max_pop_change_mean, 2); - - // Cao, Gillespie, Petzold 33. - if (mu_i[r.name] > 0) - { - tau_i[r.name] = std::min(std::abs(max_pop_change_mean / mu_i[r.name]), max_pop_change_sd / sigma_i[r.name]); - } - } - - if (tau_i.size() > 0) - { - //find min of tau_i - std::pair min; - min = *min_element(tau_i.begin(), tau_i.end(), [](const auto &lhs, const auto &rhs) - { - return lhs.second < rhs.second; - }); - - non_critical_tau = min.second; - } - - // If all reactions are non-critical, use non-critical tau. - if (critical == false) - { - tau = non_critical_tau; - } - - // If all reactions are critical, use critical tau. - else if (tau_i.size() == 0) - { - tau = critical_tau; - } - - // If there are both critical, and non critical reactions, - // Take the shortest tau between critica and non-critical. - else - { - tau = std::min(non_critical_tau, critical_tau); - } - - // If selected tau exceeds save time, integrate to save time - if (tau > 0) - { - tau = std::max(tau, 1e-10); - - if (save_time - current_time > 0) - { - tau = std::min(tau, save_time - current_time); - } - } - - else - { - tau = save_time - current_time; - } - - return tau; + interrupted = true; } std::pair, double> get_reactions( @@ -460,122 +214,3 @@ namespace Gillespy } } } - -void tau_leaper(Gillespy::Simulation* simulation, const double tau_tol){ - signal(SIGINT, signalHandler); - if(simulation){ - //Initialize your tau args - simulation->tau_args = initialize(*(simulation->model),tau_tol); - - double increment = simulation->timeline[1]-simulation->timeline[0]; - - //Initialize current_state variables, propensity_values - std::vector current_state((simulation -> model) -> number_species); - std::vector propensity_values (simulation->model->number_reactions); - - //copy initial state for each trajectory - for(unsigned int species_number = 0; species_number < ((simulation -> model) -> number_species); species_number++){ - simulation -> trajectories[0][0][species_number] = (simulation -> model) -> species[species_number].initial_population; - } - - //Simulate for each trajectory - for(unsigned int trajectory_number = 0; trajectory_number < simulation -> number_trajectories; trajectory_number++){ - if(interrupted){ - break; - } - - for (int spec = 0; spec< simulation->model->number_species; spec++){ - current_state[spec] = (simulation->model->species[spec].initial_population); - } - - - //Initialize simulation variables - simulation->current_time = 0; - unsigned int entry_count = 0; - //Propensity sum initialization, to be added to later. - double propensity_sum; - //Start save time - double save_time = 0; - //Variable to keep track of rejected steps, debug - int steps_rejected = 0; - //Initialize tau_step, will be assigned using tau::select() - double tau_step; - std::vector prev_curr_state; - - // Each save step - while (entry_count < simulation->number_timesteps){ // while less than end_time? Could be incorrect - if (interrupted) - break; - while(simulation->current_time < save_time){ - if(interrupted) // If timeout, or keyboard interrupt - break; - //calculate propensities for each step - for(unsigned int reaction_number = 0; reaction_number < ((simulation -> model) -> number_reactions); reaction_number++){ - propensity_values[reaction_number] = (simulation -> propensity_function -> TauEvaluate(reaction_number, current_state)); - } - - - tau_step = select(*(simulation->model), simulation->tau_args, tau_tol, simulation->current_time, save_time, propensity_values, current_state); // tau_step selection process - - prev_curr_state = current_state; - double prev_curr_time = simulation->current_time; - int loop_cnt = 0; - - while (true){ - loop_cnt += 1; - if (loop_cnt>100){ - throw std::runtime_error("Loop count exceeded 100, error"); - } - std::map rxn_count; - std::pair,double> values; - values = get_reactions(simulation -> model, propensity_values, tau_step, simulation->current_time, save_time); - rxn_count = values.first; // How many times reactions in model fired over a tau step - simulation->current_time = values.second; - - std::map species_modified; - for (int i = 0; imodel->number_reactions; i++){ - if (rxn_count[simulation->model->reactions[i].name] > 0) - for (auto const &spec : simulation->tau_args.reactions_reactants[i]){ - species_modified[spec] = true; - //+= for both reactants and products because current_state is represented with negative number changes for reactants, and positive for products. - current_state[spec] += simulation->model->reactions[i].species_change[spec] * rxn_count[simulation->model->reactions[i].name]; - - } - for (auto const &spec : simulation->tau_args.products[i]) - { - species_modified[spec] = true; - current_state[spec] += simulation->model->reactions[i].species_change[spec] * rxn_count[simulation->model->reactions[i].name]; - } - } - - - bool neg_state = false; - for (auto const& x : species_modified) - { - if (current_state[x.first] < 0) - neg_state = true; - } - - if (neg_state == true){ - current_state = prev_curr_state; - simulation->current_time = prev_curr_time; - tau_step /= 2; - } - - else - break; // out of while true - } - } - for (int i = 0; imodel->number_species; i++) - simulation->trajectories[trajectory_number][entry_count][i] = current_state[i]; - - save_time += increment; - entry_count += 1; - } - - } - } - } - } - - diff --git a/gillespy2/solvers/cpp/tau_hybrid_c_solver.py b/gillespy2/solvers/cpp/tau_hybrid_c_solver.py index 2043ae20d..8b344e477 100644 --- a/gillespy2/solvers/cpp/tau_hybrid_c_solver.py +++ b/gillespy2/solvers/cpp/tau_hybrid_c_solver.py @@ -10,10 +10,6 @@ class TauHybridCSolver(GillesPySolver, CSolver): target = "hybrid" def __init__(self, model: Model = None, output_directory: str = None, delete_directory: bool = True, resume=None, variable=False): - # if model is None: - # options = None - # else: - # options = TauHybridCSolver.__create_template_options(list(model.listOfSpecies.values())) options = None if model is None else TauHybridCSolver.__create_template_options(list(model.listOfSpecies.values())) super().__init__(model, output_directory, delete_directory, resume, variable, options) @@ -21,6 +17,11 @@ def __init__(self, model: Model = None, output_directory: str = None, delete_dir def __create_template_options(cls, species: "list[gillespy2.Species]"): """ Populate the given list of species modes into a set of template macro definitions. + Generated options are specific to the Tau Hybrid solver, + and get passed as custom definitons to the build engine. + + :param species: Ordered list of GillesPy2 species to generate options for. + :return: Dictionary containing key-value pairs representing macro definitions. """ species_mode_map = { "continuous": "CONTINUOUS_MODE", From be941f6af0635cecd213a97cd080c42dba896723 Mon Sep 17 00:00:00 2001 From: Joshua Cooper Date: Tue, 29 Jun 2021 18:50:21 -0400 Subject: [PATCH 76/88] Add flagging functions to solvers - Header for `hybridutils.h` - Add `flag_det_rxns` and `partition_species` to Tau Hybrid solver loop - Move construction of `HybridSpecies` and `HybridReaction` vectors out - Utility `.ps1` scripts for working on Windows --- gillespy2/solvers/cpp/c_base/.env.ps1 | 26 +++++++++++++++++++ gillespy2/solvers/cpp/c_base/clean.ps1 | 2 ++ .../tau_hybrid_cpp_solver/HybridModel.cpp | 17 +++++++++++- .../tau_hybrid_cpp_solver/HybridModel.h | 8 +++--- .../TauHybridSimulation.cpp | 11 +++----- .../tau_hybrid_cpp_solver/TauHybridSolver.cpp | 19 ++++++++++---- .../tau_hybrid_cpp_solver/hybridutils.cpp | 8 +++--- .../tau_hybrid_cpp_solver/hybridutils.h | 21 +++++++++++++++ .../tau_hybrid_cpp_solver/integrator.cpp | 14 ++-------- .../c_base/tau_hybrid_cpp_solver/integrator.h | 4 +-- .../cpp/c_base/tau_hybrid_cpp_solver/rhs.cpp | 10 +++---- 11 files changed, 97 insertions(+), 43 deletions(-) create mode 100644 gillespy2/solvers/cpp/c_base/.env.ps1 create mode 100644 gillespy2/solvers/cpp/c_base/clean.ps1 diff --git a/gillespy2/solvers/cpp/c_base/.env.ps1 b/gillespy2/solvers/cpp/c_base/.env.ps1 new file mode 100644 index 000000000..12da1137c --- /dev/null +++ b/gillespy2/solvers/cpp/c_base/.env.ps1 @@ -0,0 +1,26 @@ +function New-PathIfNotExists +{ + param( + [string] $Path + ) + + if ( -Not (Test-Path "${Path}")) + { + New-Item -ItemType Directory -Path "$Path" + return $false + } + return $true +} + +New-PathIfNotExists -Path "obj" +New-PathIfNotExists -Path "bin" + +if ( -Not (Test-Path dev)) +{ + Copy-Item -Recurse template -Destination dev +} + +$env:OBJ_DIR = "obj" +$env:TEMPLATE_DIR = "dev" +$env:OUTPUT_DIR = "bin" +$env:SOLVER = "hybrid" diff --git a/gillespy2/solvers/cpp/c_base/clean.ps1 b/gillespy2/solvers/cpp/c_base/clean.ps1 new file mode 100644 index 000000000..e5ad71b6c --- /dev/null +++ b/gillespy2/solvers/cpp/c_base/clean.ps1 @@ -0,0 +1,2 @@ +Remove-Item $env:OBJ_DIR/*.o +Remove-Item $env:OUTPUT_DIR/*.out diff --git a/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/HybridModel.cpp b/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/HybridModel.cpp index cb0fc5d17..c52cfe2f9 100644 --- a/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/HybridModel.cpp +++ b/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/HybridModel.cpp @@ -4,7 +4,7 @@ namespace Gillespy::TauHybrid { HybridReaction::HybridReaction() - : mode(SimulationState::CONTINUOUS), + : mode(SimulationState::DISCRETE), base_reaction(nullptr) { // Empty constructor body @@ -25,6 +25,21 @@ namespace Gillespy::TauHybrid // Empty constructor body } + HybridSimulation::HybridSimulation(const Model &model) + : Simulation(), + species_state(model.number_species), + reaction_state(model.number_reactions) + { + for (int spec_i = 0; spec_i < model.number_species; ++spec_i) + { + species_state[spec_i].base_species = &model.species[spec_i]; + } + + for (int rxn_i = 0; rxn_i < model.number_reactions; ++rxn_i) + { + reaction_state[rxn_i].base_reaction = &model.reactions[rxn_i]; + } + } double DifferentialEquation::evaluate( diff --git a/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/HybridModel.h b/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/HybridModel.h index a852d17ff..3ab43fcb6 100644 --- a/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/HybridModel.h +++ b/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/HybridModel.h @@ -1,6 +1,5 @@ #pragma once -#include #include #include "model.h" @@ -82,12 +81,11 @@ namespace Gillespy::TauHybrid { struct HybridSimulation : Simulation { - hybrid_state *trajectories_hybrid1D; - hybrid_state ***trajectories_hybrid; - - double hybrid_propensity(int reaction_id, std::vector ¤t_state); + std::vector species_state; + std::vector reaction_state; HybridSimulation(); + HybridSimulation(const Model &model); }; diff --git a/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/TauHybridSimulation.cpp b/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/TauHybridSimulation.cpp index 8072d3621..f7a311852 100644 --- a/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/TauHybridSimulation.cpp +++ b/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/TauHybridSimulation.cpp @@ -6,6 +6,7 @@ #include #include "TauHybridSolver.h" #include "template.h" +#include "hybrid_template.h" #include "arg_parser.h" using namespace Gillespy; @@ -29,13 +30,6 @@ class PropensityFunction : public IPropensityFunction{ double evaluate(unsigned int reaction_number, unsigned int* S){return 1.0;} }; -double Gillespy::TauHybrid::HybridSimulation::hybrid_propensity( - int reaction_id, - std::vector &S) -{ - return map_ode_propensity(reaction_id, S); -} - double Gillespy::TauHybrid::HybridReaction::ode_propensity( ReactionId reaction_number, std::vector &state) @@ -68,7 +62,7 @@ int main(int argc, char* argv[]){ } IPropensityFunction *propFun = new PropensityFunction(); //Simulation INIT - TauHybrid::HybridSimulation simulation; + TauHybrid::HybridSimulation simulation(model); simulation.model = &model; simulation.end_time = end_time; simulation.random_seed = random_seed; @@ -76,6 +70,7 @@ int main(int argc, char* argv[]){ simulation.number_trajectories = number_trajectories; simulation.propensity_function = propFun; init_simulation(&model, simulation); + Gillespy::TauHybrid::map_species_modes(simulation.species_state); // Perform ODE // TauHybrid::TauHybridCSolver(&simulation, tau_tol); simulation.output_results_buffer(std::cout); diff --git a/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/TauHybridSolver.cpp b/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/TauHybridSolver.cpp index 3f570f006..bfc2dbe1c 100644 --- a/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/TauHybridSolver.cpp +++ b/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/TauHybridSolver.cpp @@ -10,7 +10,6 @@ #include "TauHybridSolver.h" #include "HybridModel.h" #include "integrator.h" -#include "hybrid_template.h" #include "hybridutils.h" #include "tau.h" using namespace Gillespy; @@ -57,9 +56,6 @@ namespace Gillespy::TauHybrid { N_Vector y0 = init_model_vector(model, urn); Integrator sol(simulation, y0, GPY_HYBRID_RELTOL, GPY_HYBRID_ABSTOL); - // Inject user-defined species modes into the solver. - map_species_modes(sol.data.species_state); - // Initialize the species population for the trajectory. for (int spec_i = 0; spec_i < num_species; ++spec_i) { current_state[spec_i] = species[spec_i].initial_population; @@ -90,6 +86,19 @@ namespace Gillespy::TauHybrid { sol.data.propensities, current_populations ); + partition_species( + simulation->reaction_state, + simulation->species_state, + sol.data.propensities, + current_state, + tau_step, + tau_args + ); + flag_det_rxns( + simulation->reaction_state, + simulation->species_state + ); + create_differential_equations(simulation->species_state, simulation->reaction_state); // Determine what the next time point is. // This will become current_time on the next iteration. @@ -127,7 +136,7 @@ namespace Gillespy::TauHybrid { // Does not get updated unless the changes are deemed valid. double rxn_state = result.reactions[rxn_i]; - switch (sol.data.reaction_state[rxn_i].mode) { + switch (simulation->reaction_state[rxn_i].mode) { case SimulationState::DISCRETE: while (rxn_state >= 0) { // "Fire" a reaction by recording changes in dependent species. diff --git a/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/hybridutils.cpp b/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/hybridutils.cpp index aeef7ae85..58cf56028 100644 --- a/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/hybridutils.cpp +++ b/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/hybridutils.cpp @@ -1,7 +1,5 @@ #include -#include "HybridModel.h" -#include "tau.h" -#include "model.h" +#include "hybridutils.h" // toggle_reactions() namespace Gillespy::TauHybrid { @@ -50,10 +48,10 @@ namespace Gillespy::TauHybrid { } void partition_species( - std::vector &species, std::vector &reactions, + std::vector &species, const std::vector &propensity_values, - std::vector curr_state, + std::vector &curr_state, double tau_step, const TauArgs &tauArgs) { diff --git a/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/hybridutils.h b/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/hybridutils.h index e69de29bb..a13baa9fc 100644 --- a/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/hybridutils.h +++ b/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/hybridutils.h @@ -0,0 +1,21 @@ +#pragma once + +#include "HybridModel.h" +#include "tau.h" + +namespace Gillespy::TauHybrid +{ + + std::set flag_det_rxns( + std::vector &reactions, + std::vector &species); + + void partition_species( + std::vector &reactions, + std::vector &species, + const std::vector &propensity_values, + std::vector &curr_state, + double tau_step, + const TauArgs &TauArgs); + +} diff --git a/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/integrator.cpp b/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/integrator.cpp index c1d57fb52..e9a68ebe4 100644 --- a/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/integrator.cpp +++ b/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/integrator.cpp @@ -14,17 +14,8 @@ IntegratorData::IntegratorData( concentrations(std::vector(num_species)), populations(std::vector(num_species)), propensities(std::vector(num_reactions)), - species_state(num_species), - reaction_state(num_reactions) -{ - for (int spec_i = 0; spec_i < num_species; ++spec_i) { - species_state[spec_i].base_species = &simulation->model->species[spec_i]; - } - - for (int rxn_i = 0; rxn_i < num_reactions; ++rxn_i) { - reaction_state[rxn_i].base_reaction = &simulation->model->reactions[rxn_i]; - } -} + species_state(&simulation->species_state), + reaction_state(&simulation->reaction_state) {} IntegratorData::IntegratorData(HybridSimulation *simulation) : IntegratorData( @@ -97,7 +88,6 @@ Integrator::~Integrator() IntegrationResults Integrator::integrate(double *t) { - create_differential_equations(data.species_state, data.reaction_state); if (!validate(CVode(cvode_mem, *t, y, &this->t, CV_NORMAL))) { return { nullptr, nullptr }; } diff --git a/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/integrator.h b/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/integrator.h index 1bf94a13f..0cbb0b201 100644 --- a/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/integrator.h +++ b/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/integrator.h @@ -14,8 +14,8 @@ namespace Gillespy::TauHybrid struct IntegratorData { HybridSimulation *simulation; - std::vector species_state; - std::vector reaction_state; + std::vector *species_state; + std::vector *reaction_state; std::vector concentrations; std::vector populations; diff --git a/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/rhs.cpp b/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/rhs.cpp index 81879da81..a508e5ec8 100644 --- a/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/rhs.cpp +++ b/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/rhs.cpp @@ -18,8 +18,8 @@ int Gillespy::TauHybrid::rhs(realtype t, N_Vector y, N_Vector ydot, void *user_d // Extract simulation data IntegratorData *data = static_cast(user_data); HybridSimulation *sim = data->simulation; - std::vector &species = data->species_state; - std::vector &reactions = data->reaction_state; + std::vector *species = data->species_state; + std::vector *reactions = data->reaction_state; std::vector &propensities = data->propensities; // Concentrations and reactions are both used for their respective propensity evaulations. // They both should, roughly, reflect the same data, but tau selection requires both. @@ -46,16 +46,16 @@ int Gillespy::TauHybrid::rhs(realtype t, N_Vector y, N_Vector ydot, void *user_d // for each of their dependent species. // To handle these, we will go ahead and evaluate each species' differential equations. for (spec_i = 0; spec_i < num_species; ++spec_i) { - dydt[spec_i] = species[spec_i].diff_equation.evaluate(concentrations, populations); + dydt[spec_i] = (*species)[spec_i].diff_equation.evaluate(concentrations, populations); } // Process deterministic propensity state // These updates get written directly to the integrator's concentration state for (int rxn_i = 0; rxn_i < num_reactions; ++rxn_i) { - switch (reactions[rxn_i].mode) { + switch ((*reactions)[rxn_i].mode) { case SimulationState::DISCRETE: // Process stochastic reaction state by updating the root offset for each reaction. - propensity = reactions[rxn_i].ssa_propensity(rxn_i, populations); + propensity = (*reactions)[rxn_i].ssa_propensity(rxn_i, populations); dydt_offsets[rxn_i] = propensity; propensities[rxn_i] = propensity; break; From 290af737ec6294af1e417e2938f81123f057c052 Mon Sep 17 00:00:00 2001 From: Josh C Date: Thu, 1 Jul 2021 11:34:27 -0400 Subject: [PATCH 77/88] Fix ordering for reactions/products in utils --- .../tau_hybrid_cpp_solver/hybridutils.cpp | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) diff --git a/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/hybridutils.cpp b/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/hybridutils.cpp index 58cf56028..6d83192d1 100644 --- a/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/hybridutils.cpp +++ b/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/hybridutils.cpp @@ -77,25 +77,22 @@ namespace Gillespy::TauHybrid { HybridReaction &rxn = reactions[rxn_i]; for (int spec_i = 0; spec_i < species.size(); ++spec_i) { + // Only dynamic species whose mean/SD is requested are to be considered. + if (means.count(spec_i) <= 0) { + continue; + } + // Selected species is either a reactant or a product, depending on whether + // dx is positive or negative. // 0-dx species are not dependencies of this reaction, so dx == 0 is ignored. int spec_dx = rxn.base_reaction->species_change[spec_i]; - if (spec_dx > 0) { + if (spec_dx < 0) { // Selected species is a reactant. - HybridSpecies &reactant = species[spec_i]; - // Only dynamic species need to be considered. - if (reactant.partition_mode != SimulationState::DYNAMIC) { - continue; - } means[spec_i] -= (tau_step * propensity_values[rxn_i] * spec_dx); sd[spec_i] += std::pow((tau_step * propensity_values[rxn_i] * spec_dx), 2); } - else if (spec_dx < 0) { + else if (spec_dx > 0) { // Selected species is a product. HybridSpecies &product = species[spec_i]; - // Only dynamic species need to be considered. - if (product.partition_mode != SimulationState::DYNAMIC) { - continue; - } means[spec_i] += (tau_step * propensity_values[rxn_i] * spec_dx); sd[spec_i] += std::pow((tau_step * propensity_values[rxn_i] * spec_dx), 2); } From 58c6bba7fc71d92ab86cfd21eda350c70c4ae653 Mon Sep 17 00:00:00 2001 From: Josh C Date: Thu, 1 Jul 2021 12:32:51 -0400 Subject: [PATCH 78/88] Cleanup prep and consolidation - Move RHS into integrator source/header - Move hybrid utils into hybrid model source/header - Revert Makefile to parity with `develop` branch - Add `switch_min` property to hybrid template --- gillespy2/solvers/cpp/c_base/.env | 8 - gillespy2/solvers/cpp/c_base/.env.ps1 | 26 ---- gillespy2/solvers/cpp/c_base/Makefile | 23 +-- .../tau_hybrid_cpp_solver/HybridModel.cpp | 140 ++++++++++++++++++ .../tau_hybrid_cpp_solver/HybridModel.h | 22 ++- .../tau_hybrid_cpp_solver/TauHybridSolver.cpp | 2 +- .../tau_hybrid_cpp_solver/hybrid_template.cpp | 4 +- .../tau_hybrid_cpp_solver/hybridutils.cpp | 137 ----------------- .../tau_hybrid_cpp_solver/hybridutils.h | 21 --- .../tau_hybrid_cpp_solver/integrator.cpp | 67 ++++++++- .../c_base/tau_hybrid_cpp_solver/integrator.h | 2 + .../cpp/c_base/tau_hybrid_cpp_solver/rhs.cpp | 71 --------- .../cpp/c_base/tau_hybrid_cpp_solver/rhs.h | 8 - .../tau_hybrid_cpp_solver/statistics.cpp | 121 --------------- .../c_base/tau_hybrid_cpp_solver/statistics.h | 29 ---- gillespy2/solvers/cpp/tau_hybrid_c_solver.py | 2 +- 16 files changed, 234 insertions(+), 449 deletions(-) delete mode 100644 gillespy2/solvers/cpp/c_base/.env delete mode 100644 gillespy2/solvers/cpp/c_base/.env.ps1 delete mode 100644 gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/hybridutils.cpp delete mode 100644 gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/hybridutils.h delete mode 100644 gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/rhs.cpp delete mode 100644 gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/rhs.h delete mode 100644 gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/statistics.cpp delete mode 100644 gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/statistics.h diff --git a/gillespy2/solvers/cpp/c_base/.env b/gillespy2/solvers/cpp/c_base/.env deleted file mode 100644 index ccf9e0178..000000000 --- a/gillespy2/solvers/cpp/c_base/.env +++ /dev/null @@ -1,8 +0,0 @@ -mkdir -p obj -mkdir -p bin -mkdir -p dev -cp -u template/* dev -export OBJ_DIR=obj -export TEMPLATE_DIR=dev -export OUTPUT_DIR=bin -export SOLVER=hybrid diff --git a/gillespy2/solvers/cpp/c_base/.env.ps1 b/gillespy2/solvers/cpp/c_base/.env.ps1 deleted file mode 100644 index 12da1137c..000000000 --- a/gillespy2/solvers/cpp/c_base/.env.ps1 +++ /dev/null @@ -1,26 +0,0 @@ -function New-PathIfNotExists -{ - param( - [string] $Path - ) - - if ( -Not (Test-Path "${Path}")) - { - New-Item -ItemType Directory -Path "$Path" - return $false - } - return $true -} - -New-PathIfNotExists -Path "obj" -New-PathIfNotExists -Path "bin" - -if ( -Not (Test-Path dev)) -{ - Copy-Item -Recurse template -Destination dev -} - -$env:OBJ_DIR = "obj" -$env:TEMPLATE_DIR = "dev" -$env:OUTPUT_DIR = "bin" -$env:SOLVER = "hybrid" diff --git a/gillespy2/solvers/cpp/c_base/Makefile b/gillespy2/solvers/cpp/c_base/Makefile index dc10dba7a..f2e4aa9cd 100644 --- a/gillespy2/solvers/cpp/c_base/Makefile +++ b/gillespy2/solvers/cpp/c_base/Makefile @@ -2,7 +2,6 @@ CXX := g++ CFLAGS := -Wall -O3 CXXFLAGS := -std=c++14 -Wall -O3 -SOLVER ?= ssa ####################################### ### Input directories ### @@ -22,8 +21,6 @@ TAU_HYBRID_SOLVER_PATH := $(CBASE_DIR)/tau_hybrid_cpp_solver OBJ_DIR ?= $(CBASE_DIR) OUTPUT_DIR ?= $(CBASE_DIR) OUTPUT_FILE ?= $(OUTPUT_DIR)/Simulation.out -EXE_CMD ?= ./ -EXE_ARGS ?= --trajectories 1 --end 10 --timesteps 11 --increment 1 SUNDIALS_OBJ ?= $(OBJ_DIR) INCLUDES := -I$(CBASE_DIR) -I$(SUNDIALS_INC) -I$(TEMPLATE_DIR) -I$(TAU_DIR) ########################## @@ -36,7 +33,7 @@ SUNOBJ_PATHS := $(SUNOBJ:%.o=$(SUNDIALS_OBJ)/%.o) TAU_DEPENDENCIES = tau.o TAU_DEPS_PATHS := $(TAU_DEPENDENCIES:%.o=$(OBJ_DIR)/%.o) -TAU_HYBRID_DEPENDENCIES = HybridModel.o rhs.o integrator.o hybrid_template.o hybridutils.o +TAU_HYBRID_DEPENDENCIES = HybridModel.o integrator.o hybrid_template.o TAU_HYBRID_DEPS_PATHS := $(TAU_HYBRID_DEPENDENCIES:%.o=$(OBJ_DIR)/%.o) ################################### @@ -50,8 +47,8 @@ $(OBJ_DIR)/arg_parser.o: $(CBASE_DIR)/arg_parser.cpp $(CBASE_DIR)/arg_parser.h arg_parser: $(OBJ_DIR)/arg_parser.o ; $(SUNOBJ_PATHS): $(SUNDIALS_OBJ)/%.o: $(SUNDIALS_SRC)/%.c - $(CXX) -c -o $@ $< $(CXXFLAGS) -I$(SUNDIALS_INC) -sundials: $(SUNOBJ_PATHS); + $(CXX) -c -o $@ $< $(CFLAGS) -I$(SUNDIALS_INC) +sundials: $(SUNOBJ_PATHS) ; $(TAU_DEPS_PATHS): $(OBJ_DIR)/%.o: $(TAU_DIR)/%.cpp $(CXX) -c -o $@ $< $(CXXFLAGS) $(INCLUDES) @@ -98,7 +95,7 @@ $(OBJ_DIR)/TauHybridSimulation.o: $(TAU_HYBRID_SOLVER_PATH)/TauHybridSimulation. ### PRE-COMPILE RULES ### prebuild_solvers: ODESolver.o SSASolver.o TauLeapingSolver.o TauHybridSolver.o ; -prebuild_simulations: ODESimulation.o SSASimulation.o TauLeapingSimulation.o TauHybridSolver.o ; +prebuild_simulations: ODESimulation.o SSASimulation.o TauLeapingSimulation.o TauHybridSimulation.o ; prebuild: prebuild_solvers prebuild_simulations sundials arg_parser ; @@ -118,18 +115,8 @@ tau_leap: TauLeapingSimulation.o TauLeapingSolver.o tau_deps model arg_parser $( $(CXX) $(COMPILATION_ARGS) $(OBJ_DIR)/TauLeapingSimulation.o $(OBJ_DIR)/tau.o $(OBJ_DIR)/TauLeapingSolver.o hybrid: TauHybridSimulation.o TauHybridSolver.o model arg_parser sundials tau_deps hybrid_deps $(TEMPLATE_CPP) - $(CXX) $(COMPILATION_ARGS) $(SUNOBJ_PATHS) $(TAU_HYBRID_DEPS_PATHS) $(TAU_DEPS_PATHS) $(OBJ_DIR)/TauHybridSimulation.o $(OBJ_DIR)/TauHybridSolver.o + $(CXX) $(COMPILATION_ARGS) $(SUNOBJ_PATHS) $(TAU_HYBRID_DEPS_PATHS) $(TAU_DEPS_PATHS) $(OBJ_DIR)/TauHybridSimulation.o $(OBJ_DIR)/TauHybridSolver.o -build: $(SOLVER); - -run: build - ./$(OUTPUT_FILE) $(EXE_ARGS) - -debug: CXXFLAGS = -std=c++14 -Wall -g -debug: build; - gdb --args $(OUTPUT_FILE) $(EXE_ARGS) - -all: prebuild clean: rm -rf $(OUTPUT_DIR)/*.out diff --git a/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/HybridModel.cpp b/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/HybridModel.cpp index c52cfe2f9..71cdc933e 100644 --- a/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/HybridModel.cpp +++ b/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/HybridModel.cpp @@ -109,4 +109,144 @@ namespace Gillespy::TauHybrid } } + // Helper method to flag reactions that can be processed deterministically (continuous change) + // without exceeding the user-supplied tolerance + std::set flag_det_rxns( + std::vector &reactions, + std::vector &species) + { + int num_reactions = reactions.size(); + int num_species = species.size(); + std::set det_rxns; + + for (int rxn_i = 0; rxn_i < reactions.size(); ++rxn_i) { + // start with the assumption that reaction is determinstic + HybridReaction &rxn = reactions[rxn_i]; + rxn.mode = SimulationState::CONTINUOUS; + + // iterate through the dependent species of this reaction + // Loop breaks if we've already determined that it is to be marked as discrete. + for (int spec_i = 0; spec_i < num_species && rxn.mode == SimulationState::CONTINUOUS; ++spec_i) { + // Reaction has a dependency on a species if its dx is positive or negative. + // Any species with "dependency" change of 0 is by definition not a dependency. + if (rxn.base_reaction->species_change[spec_i] == 0) { + continue; + } + + // if any of the dependencies are set by the user as discrete OR + // have been set as dynamic and has not been flagged as deterministic, + // allow it to be modelled discretely + if (species[spec_i].user_mode == SimulationState::DYNAMIC) { + rxn.mode = species[spec_i].partition_mode; + } + else { + rxn.mode = species[spec_i].user_mode; + } + } + + if (rxn.mode == SimulationState::CONTINUOUS) { + det_rxns.insert(rxn_i); + } + } + + return det_rxns; + } + + void partition_species( + std::vector &reactions, + std::vector &species, + const std::vector &propensity_values, + std::vector &curr_state, + double tau_step, + const TauArgs &tauArgs) + { + // coefficient of variance- key:species id, value: cv + std::map cv; + // means + std::map means; + // standard deviation + std::map sd; + + // Initialize means and sd's + for (int spec_i = 0; spec_i < species.size(); ++spec_i) { + HybridSpecies &spec = species[spec_i]; + + if (spec.user_mode == SimulationState::DYNAMIC) { + means.insert({ spec_i, curr_state[spec_i] }); + sd.insert({ spec_i, 0 }); + } + } + + // calculate means and standard deviations for dynamic-mode species involved in reactions + for (int rxn_i = 0; rxn_i < reactions.size(); ++rxn_i) { + HybridReaction &rxn = reactions[rxn_i]; + + for (int spec_i = 0; spec_i < species.size(); ++spec_i) { + // Only dynamic species whose mean/SD is requested are to be considered. + if (means.count(spec_i) <= 0) { + continue; + } + // Selected species is either a reactant or a product, depending on whether + // dx is positive or negative. + // 0-dx species are not dependencies of this reaction, so dx == 0 is ignored. + int spec_dx = rxn.base_reaction->species_change[spec_i]; + if (spec_dx < 0) { + // Selected species is a reactant. + means[spec_i] -= (tau_step * propensity_values[rxn_i] * spec_dx); + sd[spec_i] += std::pow((tau_step * propensity_values[rxn_i] * spec_dx), 2); + } + else if (spec_dx > 0) { + // Selected species is a product. + HybridSpecies &product = species[spec_i]; + means[spec_i] += (tau_step * propensity_values[rxn_i] * spec_dx); + sd[spec_i] += std::pow((tau_step * propensity_values[rxn_i] * spec_dx), 2); + } + } + } + + // calculate coefficient of variation using means and sd + for (int spec_i = 0; spec_i < species.size(); ++spec_i) { + if (means.count(spec_i) <= 0) { + continue; + } + + HybridSpecies &spec = species[spec_i]; + if (spec.switch_min == 0) + { + // (default value means switch min not set, use switch tol) + if (means[spec_i] > 0) { + cv[spec_i] = (sd[spec_i] /means[spec_i]); + } + else { + cv[spec_i] = 1; + } + + spec.partition_mode = cv[spec_i] < spec.switch_tol + ? SimulationState::CONTINUOUS + : SimulationState::DISCRETE; + } + else + { + spec.partition_mode = means[spec_i] > spec.switch_min + ? SimulationState::CONTINUOUS + : SimulationState::DISCRETE; + } + } + } + + void update_species_state( + std::vector &species, + std::vector ¤t_state) + { + for (int spec_i = 0; spec_i < species.size(); ++spec_i) { + switch (species[spec_i].partition_mode) { + case SimulationState::CONTINUOUS: + current_state[spec_i] = std::floor(current_state[spec_i]); + break; + default: + break; + } + } + } + } diff --git a/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/HybridModel.h b/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/HybridModel.h index 3ab43fcb6..21fc16c0d 100644 --- a/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/HybridModel.h +++ b/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/HybridModel.h @@ -2,6 +2,7 @@ #include #include "model.h" +#include "tau.h" #define GPY_HYBRID_ABSTOL 1e-5 #define GPY_HYBRID_RELTOL 1e-5 @@ -73,12 +74,6 @@ namespace Gillespy::TauHybrid { double ssa_propensity(ReactionId reaction_number, std::vector &state); }; - union hybrid_state - { - unsigned int discrete; - double continuous; - }; - struct HybridSimulation : Simulation { std::vector species_state; @@ -88,6 +83,21 @@ namespace Gillespy::TauHybrid { HybridSimulation(const Model &model); }; + std::set flag_det_rxns( + std::vector &reactions, + std::vector &species); + + void partition_species( + std::vector &reactions, + std::vector &species, + const std::vector &propensity_values, + std::vector &curr_state, + double tau_step, + const TauArgs &TauArgs); + + void update_species_state( + std::vector &species, + std::vector ¤t_state); void create_differential_equations( std::vector &species, diff --git a/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/TauHybridSolver.cpp b/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/TauHybridSolver.cpp index bfc2dbe1c..7a9d7dace 100644 --- a/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/TauHybridSolver.cpp +++ b/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/TauHybridSolver.cpp @@ -10,7 +10,6 @@ #include "TauHybridSolver.h" #include "HybridModel.h" #include "integrator.h" -#include "hybridutils.h" #include "tau.h" using namespace Gillespy; @@ -98,6 +97,7 @@ namespace Gillespy::TauHybrid { simulation->reaction_state, simulation->species_state ); + update_species_state(simulation->species_state, current_state); create_differential_equations(simulation->species_state, simulation->reaction_state); // Determine what the next time point is. diff --git a/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/hybrid_template.cpp b/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/hybrid_template.cpp index b936e97d7..57e0cbb10 100644 --- a/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/hybrid_template.cpp +++ b/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/hybrid_template.cpp @@ -4,7 +4,9 @@ namespace Gillespy::TauHybrid { void map_species_modes(std::vector &species) { - #define SPECIES_MODE(spec_id, spec_mode) species[spec_id].user_mode = spec_mode; + #define SPECIES_MODE(spec_id, spec_mode, user_min) \ + species[spec_id].user_mode = spec_mode; \ + species[spec_id].switch_min = user_min; #define CONTINUOUS_MODE SimulationState::CONTINUOUS #define DISCRETE_MODE SimulationState::DISCRETE #define DYNAMIC_MODE SimulationState::DYNAMIC diff --git a/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/hybridutils.cpp b/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/hybridutils.cpp deleted file mode 100644 index 6d83192d1..000000000 --- a/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/hybridutils.cpp +++ /dev/null @@ -1,137 +0,0 @@ -#include -#include "hybridutils.h" - -// toggle_reactions() -namespace Gillespy::TauHybrid { - - // Helper method to flag reactions that can be processed deterministically (continuous change) - // without exceeding the user-supplied tolerance - std::set flag_det_rxns( - std::vector &reactions, - std::vector &species) - { - int num_reactions = reactions.size(); - int num_species = species.size(); - std::set det_rxns; - - for (int rxn_i = 0; rxn_i < reactions.size(); ++rxn_i) { - // start with the assumption that reaction is determinstic - HybridReaction &rxn = reactions[rxn_i]; - rxn.mode = SimulationState::CONTINUOUS; - - // iterate through the dependent species of this reaction - // Loop breaks if we've already determined that it is to be marked as discrete. - for (int spec_i = 0; spec_i < num_species && rxn.mode == SimulationState::CONTINUOUS; ++spec_i) { - // Reaction has a dependency on a species if its dx is positive or negative. - // Any species with "dependency" change of 0 is by definition not a dependency. - if (rxn.base_reaction->species_change[spec_i] == 0) { - continue; - } - - // if any of the dependencies are set by the user as discrete OR - // have been set as dynamic and has not been flagged as deterministic, - // allow it to be modelled discretely - if (species[spec_i].user_mode == SimulationState::DYNAMIC) { - rxn.mode = species[spec_i].partition_mode; - } - else { - rxn.mode = species[spec_i].user_mode; - } - } - - if (rxn.mode == SimulationState::CONTINUOUS) { - det_rxns.insert(rxn_i); - } - } - - return det_rxns; - } - - void partition_species( - std::vector &reactions, - std::vector &species, - const std::vector &propensity_values, - std::vector &curr_state, - double tau_step, - const TauArgs &tauArgs) - { - // coefficient of variance- key:species id, value: cv - std::map cv; - // means - std::map means; - // standard deviation - std::map sd; - - // Initialize means and sd's - for (int spec_i = 0; spec_i < species.size(); ++spec_i) { - HybridSpecies &spec = species[spec_i]; - - if (spec.user_mode == SimulationState::DYNAMIC) { - means.insert({ spec_i, curr_state[spec_i] }); - sd.insert({ spec_i, 0 }); - } - } - - // calculate means and standard deviations for dynamic-mode species involved in reactions - for (int rxn_i = 0; rxn_i < reactions.size(); ++rxn_i) { - HybridReaction &rxn = reactions[rxn_i]; - - for (int spec_i = 0; spec_i < species.size(); ++spec_i) { - // Only dynamic species whose mean/SD is requested are to be considered. - if (means.count(spec_i) <= 0) { - continue; - } - // Selected species is either a reactant or a product, depending on whether - // dx is positive or negative. - // 0-dx species are not dependencies of this reaction, so dx == 0 is ignored. - int spec_dx = rxn.base_reaction->species_change[spec_i]; - if (spec_dx < 0) { - // Selected species is a reactant. - means[spec_i] -= (tau_step * propensity_values[rxn_i] * spec_dx); - sd[spec_i] += std::pow((tau_step * propensity_values[rxn_i] * spec_dx), 2); - } - else if (spec_dx > 0) { - // Selected species is a product. - HybridSpecies &product = species[spec_i]; - means[spec_i] += (tau_step * propensity_values[rxn_i] * spec_dx); - sd[spec_i] += std::pow((tau_step * propensity_values[rxn_i] * spec_dx), 2); - } - } - } - - // calculate coefficient of variation using means and sd - for (int spec_i = 0; spec_i < species.size(); ++spec_i) { - if (means.count(spec_i) <= 0) { - continue; - } - - HybridSpecies &spec = species[spec_i]; - if (spec.switch_min == 0) - { - // (default value means switch min not set, use switch tol) - if (means[spec_i] > 0) { - cv[spec_i] = (sd[spec_i] /means[spec_i]); - } - else { - cv[spec_i] = 1; - } - - if (cv[spec_i] < spec.switch_tol) { - spec.partition_mode = SimulationState::CONTINUOUS; - } - else { - spec.partition_mode = SimulationState::DISCRETE; - } - } - else - { - if (means[spec_i] > spec.switch_min) { - spec.partition_mode = SimulationState::CONTINUOUS; - } - else { - spec.partition_mode = SimulationState::DISCRETE; - } - } - } - } -} diff --git a/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/hybridutils.h b/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/hybridutils.h deleted file mode 100644 index a13baa9fc..000000000 --- a/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/hybridutils.h +++ /dev/null @@ -1,21 +0,0 @@ -#pragma once - -#include "HybridModel.h" -#include "tau.h" - -namespace Gillespy::TauHybrid -{ - - std::set flag_det_rxns( - std::vector &reactions, - std::vector &species); - - void partition_species( - std::vector &reactions, - std::vector &species, - const std::vector &propensity_values, - std::vector &curr_state, - double tau_step, - const TauArgs &TauArgs); - -} diff --git a/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/integrator.cpp b/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/integrator.cpp index e9a68ebe4..18e729b92 100644 --- a/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/integrator.cpp +++ b/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/integrator.cpp @@ -1,5 +1,4 @@ #include "integrator.h" -#include "rhs.h" static bool validate(int retcode); @@ -152,6 +151,72 @@ N_Vector Gillespy::TauHybrid::init_model_vector(Gillespy::Model &model, URNGener return y0; } +/** + * Integrator function for ODE linear solver. + * This gets passed directly to the Sundials ODE solver once initialized. + */ +int Gillespy::TauHybrid::rhs(realtype t, N_Vector y, N_Vector ydot, void *user_data) +{ + // Get y(t) vector and f(t, y) vector + realtype *Y = N_VGetArrayPointer(y); + realtype *dydt = N_VGetArrayPointer(ydot); + realtype propensity; + + // Extract simulation data + IntegratorData *data = static_cast(user_data); + HybridSimulation *sim = data->simulation; + std::vector *species = data->species_state; + std::vector *reactions = data->reaction_state; + std::vector &propensities = data->propensities; + // Concentrations and reactions are both used for their respective propensity evaulations. + // They both should, roughly, reflect the same data, but tau selection requires both. + std::vector &concentrations = data->concentrations; + std::vector &populations = data->populations; + unsigned int num_species = sim->model->number_species; + unsigned int num_reactions = sim->model->number_reactions; + + // Differentiate different regions of the input/output vectors. + // First half is for concentrations, second half is for reaction offsets. + realtype *rxn_offsets = &Y[num_species]; + realtype *dydt_offsets = &dydt[num_species]; + int rxn_offset_boundary = num_species + num_reactions; + + // Populate the current ODE state into the concentrations vector. + // dy/dt results are initialized to zero, and become the change in propensity. + unsigned int spec_i; + for (spec_i = 0; spec_i < num_species; ++spec_i) { + concentrations[spec_i] = Y[spec_i]; + populations[spec_i] = Y[spec_i]; + } + + // Deterministic reactions generally are "evaluated" by generating dy/dt functions + // for each of their dependent species. + // To handle these, we will go ahead and evaluate each species' differential equations. + for (spec_i = 0; spec_i < num_species; ++spec_i) { + dydt[spec_i] = (*species)[spec_i].diff_equation.evaluate(concentrations, populations); + } + + // Process deterministic propensity state + // These updates get written directly to the integrator's concentration state + for (int rxn_i = 0; rxn_i < num_reactions; ++rxn_i) { + switch ((*reactions)[rxn_i].mode) { + case SimulationState::DISCRETE: + // Process stochastic reaction state by updating the root offset for each reaction. + propensity = (*reactions)[rxn_i].ssa_propensity(rxn_i, populations); + dydt_offsets[rxn_i] = propensity; + propensities[rxn_i] = propensity; + break; + + case SimulationState::CONTINUOUS: + default: + dydt_offsets[rxn_i] = 0; + break; + } + } + + return 0; +}; + bool validate(int retcode) { diff --git a/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/integrator.h b/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/integrator.h index 0cbb0b201..7650a5af1 100644 --- a/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/integrator.h +++ b/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/integrator.h @@ -97,4 +97,6 @@ namespace Gillespy::TauHybrid N_Vector init_model_vector(Model &model, URNGenerator urn); + int rhs(realtype t, N_Vector y, N_Vector ydot, void *user_data); + } diff --git a/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/rhs.cpp b/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/rhs.cpp deleted file mode 100644 index a508e5ec8..000000000 --- a/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/rhs.cpp +++ /dev/null @@ -1,71 +0,0 @@ -#include "rhs.h" -#include "HybridModel.h" -#include "integrator.h" - -using namespace Gillespy::TauHybrid; - -/** - * Integrator function for ODE linear solver. - * This gets passed directly to the Sundials ODE solver once initialized. - */ -int Gillespy::TauHybrid::rhs(realtype t, N_Vector y, N_Vector ydot, void *user_data) -{ - // Get y(t) vector and f(t, y) vector - realtype *Y = N_VGetArrayPointer(y); - realtype *dydt = N_VGetArrayPointer(ydot); - realtype propensity; - - // Extract simulation data - IntegratorData *data = static_cast(user_data); - HybridSimulation *sim = data->simulation; - std::vector *species = data->species_state; - std::vector *reactions = data->reaction_state; - std::vector &propensities = data->propensities; - // Concentrations and reactions are both used for their respective propensity evaulations. - // They both should, roughly, reflect the same data, but tau selection requires both. - std::vector &concentrations = data->concentrations; - std::vector &populations = data->populations; - unsigned int num_species = sim->model->number_species; - unsigned int num_reactions = sim->model->number_reactions; - - // Differentiate different regions of the input/output vectors. - // First half is for concentrations, second half is for reaction offsets. - realtype *rxn_offsets = &Y[num_species]; - realtype *dydt_offsets = &dydt[num_species]; - int rxn_offset_boundary = num_species + num_reactions; - - // Populate the current ODE state into the concentrations vector. - // dy/dt results are initialized to zero, and become the change in propensity. - unsigned int spec_i; - for (spec_i = 0; spec_i < num_species; ++spec_i) { - concentrations[spec_i] = Y[spec_i]; - populations[spec_i] = Y[spec_i]; - } - - // Deterministic reactions generally are "evaluated" by generating dy/dt functions - // for each of their dependent species. - // To handle these, we will go ahead and evaluate each species' differential equations. - for (spec_i = 0; spec_i < num_species; ++spec_i) { - dydt[spec_i] = (*species)[spec_i].diff_equation.evaluate(concentrations, populations); - } - - // Process deterministic propensity state - // These updates get written directly to the integrator's concentration state - for (int rxn_i = 0; rxn_i < num_reactions; ++rxn_i) { - switch ((*reactions)[rxn_i].mode) { - case SimulationState::DISCRETE: - // Process stochastic reaction state by updating the root offset for each reaction. - propensity = (*reactions)[rxn_i].ssa_propensity(rxn_i, populations); - dydt_offsets[rxn_i] = propensity; - propensities[rxn_i] = propensity; - break; - - case SimulationState::CONTINUOUS: - default: - dydt_offsets[rxn_i] = 0; - break; - } - } - - return 0; -}; diff --git a/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/rhs.h b/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/rhs.h deleted file mode 100644 index b0b106bd1..000000000 --- a/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/rhs.h +++ /dev/null @@ -1,8 +0,0 @@ -#pragma once - -#include "cvode.h" - -namespace Gillespy::TauHybrid -{ - int rhs(realtype t, N_Vector y, N_Vector ydot, void *user_data); -} diff --git a/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/statistics.cpp b/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/statistics.cpp deleted file mode 100644 index 4af940d58..000000000 --- a/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/statistics.cpp +++ /dev/null @@ -1,121 +0,0 @@ -#include "statistics.h" -#include - -namespace Gillespy::TauHybrid::Statistics -{ - - void init_species_mode(const Model &model, Simulation &simulation){ - int num_species = model.number_species; - // int num_det_species = 0; - for (int s = 0; s < num_species; s++){ - // if the user chooses discrete, initialise the partition flag to such. - if (model.species[s].user_mode == DISCRETE){ - model.species[s].partition_mode = DISCRETE; - } - // otherwise, either the user chose continuous or dynamic (or null). - // in any case, just initialise to continuous. - else { - model.species[s].partition_mode = CONTINUOUS; - // num_det_species++; - } - } - // simulation.number_det_species = num_det_species; - } - - void partition_species(const Model &model, const std::vector &propensity_values, std::vector curr_state, double tau_step, double current_time, std::map &det_species){ - // coefficient of variance- key:species id, value: cv - std::map cv; - // means - std::map means; - // standard deviation - std::map sd; - //init means - for (int i = 0; i < model.number_species; ++i){ - if (model.species[i].user_mode == DYNAMIC){ - if (model.species[i].partition_mode == CONTINUOUS){ - means.insert({i, curr_state[i].continuous}); - }else { - means.insert({i, curr_state[i].discrete}); - } - } - } - //init sd's - for (int i = 0; i < model.number_species; ++i){ - if (model.species[i].user_mode == DYNAMIC){ - sd.insert({i, 0}); - } - } - // calculate means and standard deviations for dynamic-mode species involved in reactions - for (int r = 0; r < model.number_reactions; ++r){ - for (int s = 0; s < model.number_species; ++s){ - // access list of species by accessing the correct element of the state-change vector (of the reaction) - if (model.species[model.reactions[r].species_change[s]].user_mode == DYNAMIC ){ - // if less than 0, that means this is a reactant - if (model.reactions[r].species_change[s] < 0){ - means[s] -= (tau_step * propensity_values[r] * model.reactions[r].species_change[s]); - sd[s] += std::pow((tau_step * propensity_values[r] * model.reactions[r].species_change[s]),2); - } - // if greater than 0, that means this is a product - if (model.reactions[r].species_change[s] > 0){ - means[s] += (tau_step * propensity_values[r] * model.reactions[r].species_change[s]); - sd[s] += std::pow((tau_step * propensity_values[r] * model.reactions[r].species_change[s]),2); - } - } - } - } - // calculate coefficient of variation using means and sd - - for (int s = 0; s < model.number_species; ++s){ - if (means.count(s) > 0){ - Species sref = model.species[s]; - if (sref.switch_min == 0) { // (default value means switch min not set, use switch tol) - if (means[s] > 0){ - cv[s] = sd[s] / means[s]; - }else{ - cv[s] = 1; - } - if (cv[s] < sref.switch_tol){ - sref.partition_mode == CONTINUOUS; - }else{ - sref.partition_mode == DISCRETE; - } - }else{ - if (means[s] > sref.switch_min){ - sref.partition_mode == CONTINUOUS; - }else{ - sref.partition_mode == DISCRETE; - } - } - } - } - - return; // TODO - } - - - std::pair, double> get_reactions(const Gillespy::Model *model, const std::vector &propensity_values, double tau_step, double current_time, double save_time) - { - /* Helper Function to get reactions fired from t to t+tau. Affects two values: - * rxn_count - dict with key=Reaction channel value=number of times fired - * curr_time - float representing current time - */ - - if (current_time + tau_step > save_time) - tau_step = save_time - current_time; - - std::map rxn_count; // map of how many times reaction is fired - std::random_device rd; - std::mt19937 generator(rd()); - std::pair, double> values; // value pair to be returned, map of times {map of times reaction fired, current time} - - for (int i = 0; i < model->number_reactions; i++) - { - std::poisson_distribution poisson(propensity_values[i] * tau_step); - rxn_count[model->reactions[i].name] = poisson(generator); - } - current_time = current_time + tau_step; - values.first = rxn_count; - values.second = current_time; - return values; - } -} diff --git a/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/statistics.h b/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/statistics.h deleted file mode 100644 index 31d8465bd..000000000 --- a/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/statistics.h +++ /dev/null @@ -1,29 +0,0 @@ -#pragma once - -#include "HybridModel.h" -#include -#include - -namespace Gillespy::TauHybrid::Statistics -{ - - void init_species_mode(const Model &model, Simulation &simulation); - - void partition_species( - const Model &model, - const std::vector &propensity_values, - std::vector current_state, - double tau_step, - double current_time, - std::map &det_species - ); - - std::pair, double> get_reactions( - const Model *model, - const std::vector &propensity_values, - double tau_step, - double current_time, - double save_time - ); - -} diff --git a/gillespy2/solvers/cpp/tau_hybrid_c_solver.py b/gillespy2/solvers/cpp/tau_hybrid_c_solver.py index 8b344e477..695c23350 100644 --- a/gillespy2/solvers/cpp/tau_hybrid_c_solver.py +++ b/gillespy2/solvers/cpp/tau_hybrid_c_solver.py @@ -33,7 +33,7 @@ def __create_template_options(cls, species: "list[gillespy2.Species]"): for spec_id, spec in enumerate(species): # Continuous by default mode_keyword = species_mode_map.get(spec.mode, species_mode_map["continuous"]) - species_mode_list.append(f"SPECIES_MODE({spec_id},{mode_keyword})") + species_mode_list.append(f"SPECIES_MODE({spec_id},{mode_keyword},{spec.switch_min})") return { f"GPY_HYBRID_SPECIES_MODES": " ".join(species_mode_list) From 9da398300aac1c9d5093bcd6138e3ded4b538d41 Mon Sep 17 00:00:00 2001 From: Josh C Date: Thu, 1 Jul 2021 12:59:50 -0400 Subject: [PATCH 79/88] Update test suite to include hybrid solver - Fix `initial_values` arg to reflect new arg parser name (`init_pop`) - Add `TauHybridCSolver` class to variable and non-variable test cases --- gillespy2/solvers/cpp/tau_hybrid_c_solver.py | 2 +- test/test_c_solvers.py | 4 ++++ test/test_variable_solvers.py | 8 ++++++-- 3 files changed, 11 insertions(+), 3 deletions(-) diff --git a/gillespy2/solvers/cpp/tau_hybrid_c_solver.py b/gillespy2/solvers/cpp/tau_hybrid_c_solver.py index 695c23350..19522cc82 100644 --- a/gillespy2/solvers/cpp/tau_hybrid_c_solver.py +++ b/gillespy2/solvers/cpp/tau_hybrid_c_solver.py @@ -81,7 +81,7 @@ def run(self=None, model: Model = None, t: int = 20, number_of_trajectories: int parameter_values = cutils.change_param_values(model.listOfParameters, self.parameters, model.volume, variables) args.update({ - "initial_values": populations, + "init_pop": populations, "parameters": parameter_values }) diff --git a/test/test_c_solvers.py b/test/test_c_solvers.py index da1305074..f9dd36083 100644 --- a/test/test_c_solvers.py +++ b/test/test_c_solvers.py @@ -5,6 +5,7 @@ import example_models from gillespy2.solvers.cpp.c_decoder import BasicSimDecoder from gillespy2.solvers.cpp import SSACSolver, ODECSolver, TauLeapingCSolver +from gillespy2.solvers.cpp import TauHybridCSolver class TestCSolvers(unittest.TestCase): """ @@ -19,16 +20,19 @@ class TestCSolvers(unittest.TestCase): SSACSolver, ODECSolver, TauLeapingCSolver, + TauHybridCSolver, ] solvers = { SSACSolver.target: SSACSolver(model=test_model), ODECSolver.target: ODECSolver(model=test_model), TauLeapingCSolver.target: TauLeapingCSolver(model=test_model), + TauHybridCSolver.target: TauHybridCSolver(model=test_model) } solvers_variable = { SSACSolver.target: SSACSolver(model=test_model, variable=True), ODECSolver.target: ODECSolver(model=test_model, variable=True), TauLeapingCSolver.target: TauLeapingCSolver(model=test_model, variable=True), + TauHybridCSolver.target: TauHybridCSolver(model=test_model, variable=True), } def test_c_decoder(self): diff --git a/test/test_variable_solvers.py b/test/test_variable_solvers.py index e34833563..05619596d 100644 --- a/test/test_variable_solvers.py +++ b/test/test_variable_solvers.py @@ -3,6 +3,7 @@ from gillespy2.core.gillespyError import DirectoryError, SimulationError from example_models import Example from gillespy2 import SSACSolver, ODECSolver, TauLeapingCSolver +from gillespy2 import TauHybridCSolver class TestVariableSolvers(unittest.TestCase): @@ -10,13 +11,15 @@ class TestVariableSolvers(unittest.TestCase): solverSSAC = SSACSolver(model, variable=True) solverODEC = ODECSolver(model, variable=True) solverTAUC = TauLeapingCSolver(model, variable=True) - solverlist = [solverSSAC, solverODEC, solverTAUC] + solverHYBC = TauHybridCSolver(model, variable=True) + solverlist = [solverSSAC, solverODEC, solverTAUC, solverHYBC] def test_create(self): model = Example() solverSSAC = SSACSolver(model) solverODEC = ODECSolver(model) solverTAUC = TauLeapingCSolver(model) + solverHYBC = TauHybridCSolver(model) def test_file_with_directory_name_exists(self): with self.assertRaises(DirectoryError): @@ -25,6 +28,7 @@ def test_file_with_directory_name_exists(self): solverSSAC = SSACSolver(model, temp.name) solverODEC = ODECSolver(model, temp.name) solverTAUC = TauLeapingCSolver(model, temp.name) + solverHYBC = TauHybridCSolver(model, temp.name) def test_run_example_precompiled(self): for solver in self.solverlist: @@ -54,7 +58,7 @@ def test_invalid_variable(self): results = self.model.run(solver=solver, variables={'foobar':0}) def test_run_example(self): - notPrecompiled = [SSACSolver, ODECSolver, TauLeapingCSolver] + notPrecompiled = [SSACSolver, ODECSolver, TauLeapingCSolver, TauHybridCSolver] for solver in notPrecompiled: results = self.model.run(solver=solver) From 8a810b68998236bee5957f746f6bf5fa61dc8637 Mon Sep 17 00:00:00 2001 From: Josh C Date: Thu, 1 Jul 2021 13:43:30 -0400 Subject: [PATCH 80/88] Merge prep from develop - Add template arguments to tau methods - Update data structures to use tau templates --- gillespy2/solvers/cpp/c_base/Tau/tau.cpp | 32 ++++++++++++-- gillespy2/solvers/cpp/c_base/Tau/tau.h | 42 +++++++++++++++++-- .../tau_hybrid_cpp_solver/HybridModel.cpp | 4 +- .../tau_hybrid_cpp_solver/HybridModel.h | 6 +-- .../TauHybridSimulation.cpp | 2 +- .../tau_hybrid_cpp_solver/TauHybridSolver.cpp | 6 +-- .../tau_hybrid_cpp_solver/integrator.cpp | 2 +- .../c_base/tau_hybrid_cpp_solver/integrator.h | 2 +- .../TauLeapingSimulation.cpp | 20 ++++++++- .../TauLeapingSolver.cpp | 22 +++++++++- .../tau_leaping_cpp_solver/TauLeapingSolver.h | 18 ++++++++ 11 files changed, 134 insertions(+), 22 deletions(-) diff --git a/gillespy2/solvers/cpp/c_base/Tau/tau.cpp b/gillespy2/solvers/cpp/c_base/Tau/tau.cpp index 768eb4d1d..3cc08e619 100644 --- a/gillespy2/solvers/cpp/c_base/Tau/tau.cpp +++ b/gillespy2/solvers/cpp/c_base/Tau/tau.cpp @@ -4,10 +4,11 @@ namespace Gillespy { - TauArgs initialize(Gillespy::Model &model, double tau_tol) + template + TauArgs initialize(Gillespy::Model &model, double tau_tol) { // Initialize TauArgs struct to be returned as a pointer - TauArgs tau_args; + TauArgs tau_args; // Initialize highest order rxns to 0 for (int i = 0; i < model.number_species; i++) @@ -88,9 +89,10 @@ namespace Gillespy { return tau_args; } + template double select( - Gillespy::Model &model, - TauArgs &tau_args, + Gillespy::Model &model, + TauArgs &tau_args, const double &tau_tol, const double ¤t_time, const double &save_time, @@ -237,4 +239,26 @@ namespace Gillespy { return tau; } + + // Explicitly instantiate initialize/select functions for DISCRETE simulations + template TauArgs initialize(Model &model, double tau_tol); + template double select( + Model &model, + TauArgs &tau_args, + const double &tau_tol, + const double ¤t_time, + const double &save_time, + const std::vector &propensity_values, + const std::vector ¤t_state); + + // Explicitly instantiate initialize/select functions for HYBRID simulations + template TauArgs initialize(Model &model, double tau_tol); + template double select( + Model &model, + TauArgs &tau_args, + const double &tau_tol, + const double ¤t_time, + const double &save_time, + const std::vector &propensity_values, + const std::vector ¤t_state); } diff --git a/gillespy2/solvers/cpp/c_base/Tau/tau.h b/gillespy2/solvers/cpp/c_base/Tau/tau.h index 349a2341b..1cda2b408 100644 --- a/gillespy2/solvers/cpp/c_base/Tau/tau.h +++ b/gillespy2/solvers/cpp/c_base/Tau/tau.h @@ -10,11 +10,13 @@ namespace Gillespy { + + template struct TauArgs { //Highest Order Reaction std::map HOR; - std::set reactants; + std::set> reactants; //Below are g_i_lambdas, pop element when used std::map> g_i_lambdas; std::map g_i; @@ -23,10 +25,42 @@ namespace Gillespy { std::map> products; int critical_threshold = 10; }; - TauArgs initialize(Gillespy::Model &model, double tau_tol); - double select(Gillespy::Model &model, TauArgs &tau_args, const double &tau_tol, const double ¤t_time, const double &save_time, const std::vector &propensity_values, const std::vector ¤t_state); + template + TauArgs initialize(Model &model, double tau_tol); + + template + double select( + Model &model, + TauArgs &tau_args, + const double &tau_tol, + const double ¤t_time, + const double &save_time, + const std::vector &propensity_values, + const std::vector ¤t_state); + + // Continuous tau args is used by hybrid solver + template struct TauArgs; + extern template TauArgs initialize(Model &model, double tau_tol); + extern template double select( + Model &model, + TauArgs &tau_args, + const double &tau_tol, + const double ¤t_time, + const double &save_time, + const std::vector &propensity_values, + const std::vector ¤t_state); - + // Discrete tau args is used by tau leaping solver + template struct TauArgs; + extern template TauArgs initialize(Model &model, double tau_tol); + extern template double select( + Model &model, + TauArgs &tau_args, + const double &tau_tol, + const double ¤t_time, + const double &save_time, + const std::vector &propensity_values, + const std::vector ¤t_state); } #endif //TAU_H \ No newline at end of file diff --git a/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/HybridModel.cpp b/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/HybridModel.cpp index 71cdc933e..f05d1bc53 100644 --- a/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/HybridModel.cpp +++ b/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/HybridModel.cpp @@ -25,7 +25,7 @@ namespace Gillespy::TauHybrid // Empty constructor body } - HybridSimulation::HybridSimulation(const Model &model) + HybridSimulation::HybridSimulation(const Model &model) : Simulation(), species_state(model.number_species), reaction_state(model.number_reactions) @@ -158,7 +158,7 @@ namespace Gillespy::TauHybrid const std::vector &propensity_values, std::vector &curr_state, double tau_step, - const TauArgs &tauArgs) + const TauArgs &tauArgs) { // coefficient of variance- key:species id, value: cv std::map cv; diff --git a/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/HybridModel.h b/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/HybridModel.h index 21fc16c0d..51f7918b4 100644 --- a/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/HybridModel.h +++ b/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/HybridModel.h @@ -34,7 +34,7 @@ namespace Gillespy::TauHybrid { struct HybridSpecies { - Species *base_species; + Species *base_species; // allows the user to specify if a species' population should definitely be modeled continuously or // discretely @@ -80,7 +80,7 @@ namespace Gillespy::TauHybrid { std::vector reaction_state; HybridSimulation(); - HybridSimulation(const Model &model); + HybridSimulation(const Model &model); }; std::set flag_det_rxns( @@ -93,7 +93,7 @@ namespace Gillespy::TauHybrid { const std::vector &propensity_values, std::vector &curr_state, double tau_step, - const TauArgs &TauArgs); + const TauArgs &TauArgs); void update_species_state( std::vector &species, diff --git a/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/TauHybridSimulation.cpp b/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/TauHybridSimulation.cpp index f7a311852..6faf72920 100644 --- a/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/TauHybridSimulation.cpp +++ b/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/TauHybridSimulation.cpp @@ -54,7 +54,7 @@ int main(int argc, char* argv[]){ number_timesteps = parser.timesteps; tau_tol = parser.tau_tol; - Model model(species_names, species_populations, reaction_names); + Model model(species_names, species_populations, reaction_names); add_reactions(model); if(seed_time){ diff --git a/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/TauHybridSolver.cpp b/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/TauHybridSolver.cpp index 7a9d7dace..e5de158ed 100644 --- a/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/TauHybridSolver.cpp +++ b/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/TauHybridSolver.cpp @@ -27,15 +27,15 @@ namespace Gillespy::TauHybrid { return; } - Model &model = *(simulation->model); + Model &model = *(simulation->model); int num_species = model.number_species; int num_reactions = model.number_reactions; int num_trajectories = simulation->number_trajectories; - std::unique_ptr &species = model.species; + std::unique_ptr[]> &species = model.species; double increment = simulation->timeline[1] - simulation->timeline[0]; // Tau selector initialization. Used to select a valid tau step. - TauArgs tau_args = initialize(model, tau_tol); + TauArgs tau_args = initialize(model, tau_tol); //copy initial state for each trajectory for(int s = 0; s < num_species; s++){ diff --git a/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/integrator.cpp b/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/integrator.cpp index 18e729b92..f605318fb 100644 --- a/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/integrator.cpp +++ b/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/integrator.cpp @@ -118,7 +118,7 @@ double URNGenerator::next() /* Initialize a SUNDials N_Vector based on information provided in the model. * */ -N_Vector Gillespy::TauHybrid::init_model_vector(Gillespy::Model &model, URNGenerator urn) +N_Vector Gillespy::TauHybrid::init_model_vector(Gillespy::Model &model, URNGenerator urn) { int rxn_offset_boundary = model.number_reactions + model.number_species; diff --git a/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/integrator.h b/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/integrator.h index 7650a5af1..bf9632ea8 100644 --- a/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/integrator.h +++ b/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/integrator.h @@ -95,7 +95,7 @@ namespace Gillespy::TauHybrid URNGenerator(double seed); }; - N_Vector init_model_vector(Model &model, URNGenerator urn); + N_Vector init_model_vector(Model &model, URNGenerator urn); int rhs(realtype t, N_Vector y, N_Vector ydot, void *user_data); diff --git a/gillespy2/solvers/cpp/c_base/tau_leaping_cpp_solver/TauLeapingSimulation.cpp b/gillespy2/solvers/cpp/c_base/tau_leaping_cpp_solver/TauLeapingSimulation.cpp index be6614f1b..f544dca2a 100644 --- a/gillespy2/solvers/cpp/c_base/tau_leaping_cpp_solver/TauLeapingSimulation.cpp +++ b/gillespy2/solvers/cpp/c_base/tau_leaping_cpp_solver/TauLeapingSimulation.cpp @@ -1,3 +1,21 @@ +/* + * GillesPy2 is a modeling toolkit for biochemical simulation. + * Copyright (C) 2019-2021 GillesPy2 developers. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + #include #include #include @@ -55,7 +73,7 @@ int main(int argc, char* argv[]){ number_timesteps = parser.timesteps; tau_tol = parser.tau_tol; - Model model(species_names, species_populations, reaction_names); + Model model(species_names, species_populations, reaction_names); add_reactions(model); if(seed_time) { diff --git a/gillespy2/solvers/cpp/c_base/tau_leaping_cpp_solver/TauLeapingSolver.cpp b/gillespy2/solvers/cpp/c_base/tau_leaping_cpp_solver/TauLeapingSolver.cpp index b3b16ffe5..8374a5661 100644 --- a/gillespy2/solvers/cpp/c_base/tau_leaping_cpp_solver/TauLeapingSolver.cpp +++ b/gillespy2/solvers/cpp/c_base/tau_leaping_cpp_solver/TauLeapingSolver.cpp @@ -1,3 +1,21 @@ +/* + * GillesPy2 is a modeling toolkit for biochemical simulation. + * Copyright (C) 2019-2021 GillesPy2 developers. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + #include #include #include @@ -24,7 +42,7 @@ namespace Gillespy } std::pair, double> get_reactions( - const Gillespy::Model *model, + const Gillespy::Model *model, const std::vector &propensity_values, double tau_step, double current_time, @@ -68,7 +86,7 @@ namespace Gillespy } //Initialize your tau args - TauArgs tau_args = initialize(*(simulation->model), tau_tol); + TauArgs tau_args = initialize(*(simulation->model), tau_tol); double increment = simulation->timeline[1] - simulation->timeline[0]; diff --git a/gillespy2/solvers/cpp/c_base/tau_leaping_cpp_solver/TauLeapingSolver.h b/gillespy2/solvers/cpp/c_base/tau_leaping_cpp_solver/TauLeapingSolver.h index 95a70ec58..4fd9dfdb6 100644 --- a/gillespy2/solvers/cpp/c_base/tau_leaping_cpp_solver/TauLeapingSolver.h +++ b/gillespy2/solvers/cpp/c_base/tau_leaping_cpp_solver/TauLeapingSolver.h @@ -1,3 +1,21 @@ +/* + * GillesPy2 is a modeling toolkit for biochemical simulation. + * Copyright (C) 2019-2021 GillesPy2 developers. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + #pragma once #include "model.h" From f57f81cb574f3610d8c5d7209ad3e535f1fdd8d2 Mon Sep 17 00:00:00 2001 From: Josh C Date: Thu, 1 Jul 2021 13:52:01 -0400 Subject: [PATCH 81/88] Add license headers to hybrid files --- gillespy2/solvers/cpp/c_base/Tau/tau.cpp | 19 +++++++++++++++++++ gillespy2/solvers/cpp/c_base/Tau/tau.h | 18 ++++++++++++++++++ .../tau_hybrid_cpp_solver/HybridModel.cpp | 18 ++++++++++++++++++ .../tau_hybrid_cpp_solver/HybridModel.h | 18 ++++++++++++++++++ .../TauHybridSimulation.cpp | 18 ++++++++++++++++++ .../tau_hybrid_cpp_solver/TauHybridSolver.cpp | 18 ++++++++++++++++++ .../tau_hybrid_cpp_solver/TauHybridSolver.h | 18 ++++++++++++++++++ .../tau_hybrid_cpp_solver/hybrid_template.cpp | 18 ++++++++++++++++++ .../tau_hybrid_cpp_solver/hybrid_template.h | 18 ++++++++++++++++++ .../tau_hybrid_cpp_solver/integrator.cpp | 18 ++++++++++++++++++ .../c_base/tau_hybrid_cpp_solver/integrator.h | 18 ++++++++++++++++++ 11 files changed, 199 insertions(+) diff --git a/gillespy2/solvers/cpp/c_base/Tau/tau.cpp b/gillespy2/solvers/cpp/c_base/Tau/tau.cpp index 3cc08e619..612502214 100644 --- a/gillespy2/solvers/cpp/c_base/Tau/tau.cpp +++ b/gillespy2/solvers/cpp/c_base/Tau/tau.cpp @@ -1,3 +1,22 @@ +/* + * GillesPy2 is a modeling toolkit for biochemical simulation. + * Copyright (C) 2019-2021 GillesPy2 developers. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + + #include "tau.h" #include diff --git a/gillespy2/solvers/cpp/c_base/Tau/tau.h b/gillespy2/solvers/cpp/c_base/Tau/tau.h index 1cda2b408..fe7f2fe7f 100644 --- a/gillespy2/solvers/cpp/c_base/Tau/tau.h +++ b/gillespy2/solvers/cpp/c_base/Tau/tau.h @@ -1,3 +1,21 @@ +/* + * GillesPy2 is a modeling toolkit for biochemical simulation. + * Copyright (C) 2019-2021 GillesPy2 developers. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + #ifndef TAU_H #define TAU_H diff --git a/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/HybridModel.cpp b/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/HybridModel.cpp index f05d1bc53..932a7837a 100644 --- a/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/HybridModel.cpp +++ b/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/HybridModel.cpp @@ -1,3 +1,21 @@ +/* + * GillesPy2 is a modeling toolkit for biochemical simulation. + * Copyright (C) 2019-2021 GillesPy2 developers. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + #include "HybridModel.h" namespace Gillespy::TauHybrid diff --git a/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/HybridModel.h b/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/HybridModel.h index 51f7918b4..11a1d9801 100644 --- a/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/HybridModel.h +++ b/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/HybridModel.h @@ -1,3 +1,21 @@ +/* + * GillesPy2 is a modeling toolkit for biochemical simulation. + * Copyright (C) 2019-2021 GillesPy2 developers. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + #pragma once #include diff --git a/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/TauHybridSimulation.cpp b/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/TauHybridSimulation.cpp index 6faf72920..ee93de63d 100644 --- a/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/TauHybridSimulation.cpp +++ b/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/TauHybridSimulation.cpp @@ -1,3 +1,21 @@ +/* + * GillesPy2 is a modeling toolkit for biochemical simulation. + * Copyright (C) 2019-2021 GillesPy2 developers. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + #include #include #include diff --git a/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/TauHybridSolver.cpp b/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/TauHybridSolver.cpp index e5de158ed..b445b50a5 100644 --- a/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/TauHybridSolver.cpp +++ b/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/TauHybridSolver.cpp @@ -1,3 +1,21 @@ +/* + * GillesPy2 is a modeling toolkit for biochemical simulation. + * Copyright (C) 2019-2021 GillesPy2 developers. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + #include #include //Included for timeout signal handling #include diff --git a/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/TauHybridSolver.h b/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/TauHybridSolver.h index 3c340402d..2ff7d74d9 100644 --- a/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/TauHybridSolver.h +++ b/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/TauHybridSolver.h @@ -1,3 +1,21 @@ +/* + * GillesPy2 is a modeling toolkit for biochemical simulation. + * Copyright (C) 2019-2021 GillesPy2 developers. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + #ifndef TAUHYBRIDCSOLVER_H #define TAUHYBRIDCSOLVER_H #include "HybridModel.h" diff --git a/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/hybrid_template.cpp b/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/hybrid_template.cpp index 57e0cbb10..80f81f9da 100644 --- a/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/hybrid_template.cpp +++ b/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/hybrid_template.cpp @@ -1,3 +1,21 @@ +/* + * GillesPy2 is a modeling toolkit for biochemical simulation. + * Copyright (C) 2019-2021 GillesPy2 developers. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + #include "hybrid_template.h" namespace Gillespy::TauHybrid diff --git a/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/hybrid_template.h b/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/hybrid_template.h index 4d18f68e6..20099d4a3 100644 --- a/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/hybrid_template.h +++ b/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/hybrid_template.h @@ -1,3 +1,21 @@ +/* + * GillesPy2 is a modeling toolkit for biochemical simulation. + * Copyright (C) 2019-2021 GillesPy2 developers. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + #pragma once #define GPY_SOLVER_HYBRID diff --git a/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/integrator.cpp b/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/integrator.cpp index f605318fb..dc752a7bd 100644 --- a/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/integrator.cpp +++ b/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/integrator.cpp @@ -1,3 +1,21 @@ +/* + * GillesPy2 is a modeling toolkit for biochemical simulation. + * Copyright (C) 2019-2021 GillesPy2 developers. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + #include "integrator.h" static bool validate(int retcode); diff --git a/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/integrator.h b/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/integrator.h index bf9632ea8..74e245316 100644 --- a/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/integrator.h +++ b/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/integrator.h @@ -1,3 +1,21 @@ +/* + * GillesPy2 is a modeling toolkit for biochemical simulation. + * Copyright (C) 2019-2021 GillesPy2 developers. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + #pragma once #include "HybridModel.h" From 5d3df8b0a8dd8b08ed03b4b4369600a0cc184499 Mon Sep 17 00:00:00 2001 From: Joshua Cooper Date: Sun, 4 Jul 2021 11:25:22 -0400 Subject: [PATCH 82/88] PR review --- gillespy2/solvers/cpp/build/build_engine.py | 6 +-- gillespy2/solvers/cpp/c_base/Tau/tau.cpp | 35 +++++++------- gillespy2/solvers/cpp/c_base/Tau/tau.h | 7 ++- .../tau_hybrid_cpp_solver/HybridModel.h | 7 +-- .../TauHybridSimulation.cpp | 11 +++-- .../tau_hybrid_cpp_solver/TauHybridSolver.cpp | 47 ++++++++++++------- .../tau_hybrid_cpp_solver/TauHybridSolver.h | 9 ++-- 7 files changed, 69 insertions(+), 53 deletions(-) diff --git a/gillespy2/solvers/cpp/build/build_engine.py b/gillespy2/solvers/cpp/build/build_engine.py index 82e1653cb..42d7e5e9c 100644 --- a/gillespy2/solvers/cpp/build/build_engine.py +++ b/gillespy2/solvers/cpp/build/build_engine.py @@ -69,7 +69,7 @@ def get_missing_dependencies(cls) -> "list[str]": return missing - def prepare(self, model: Model, variable=False, cusotm_definitions: "dict[str, str]" = None) -> str: + def prepare(self, model: Model, variable=False, custom_definitions: "dict[str, str]" = None) -> str: """ Prepare the template directory for compilation. The following operations will be performed: @@ -110,10 +110,10 @@ def prepare(self, model: Model, variable=False, cusotm_definitions: "dict[str, s template_file = self.template_dir.joinpath(self.template_definitions_name) template_file.unlink() template_gen.write_template(str(template_file), model, variable) - if cusotm_definitions is not None: + if custom_definitions is not None: options_file = self.template_dir.joinpath(self.template_options_name) options_file.unlink() - template_gen.write_definitions(str(options_file), cusotm_definitions) + template_gen.write_definitions(str(options_file), custom_definitions) # With all required information gathered, create a Make instance. self.make = Make(str(self.makefile), str(self.output_dir), str(self.obj_dir)) diff --git a/gillespy2/solvers/cpp/c_base/Tau/tau.cpp b/gillespy2/solvers/cpp/c_base/Tau/tau.cpp index 612502214..271bb1446 100644 --- a/gillespy2/solvers/cpp/c_base/Tau/tau.cpp +++ b/gillespy2/solvers/cpp/c_base/Tau/tau.cpp @@ -21,7 +21,8 @@ #include -namespace Gillespy { +namespace Gillespy +{ template TauArgs initialize(Gillespy::Model &model, double tau_tol) @@ -261,23 +262,23 @@ namespace Gillespy { // Explicitly instantiate initialize/select functions for DISCRETE simulations template TauArgs initialize(Model &model, double tau_tol); - template double select( - Model &model, - TauArgs &tau_args, - const double &tau_tol, - const double ¤t_time, - const double &save_time, - const std::vector &propensity_values, - const std::vector ¤t_state); + template double select( + Model &model, + TauArgs &tau_args, + const double &tau_tol, + const double ¤t_time, + const double &save_time, + const std::vector &propensity_values, + const std::vector ¤t_state); // Explicitly instantiate initialize/select functions for HYBRID simulations template TauArgs initialize(Model &model, double tau_tol); - template double select( - Model &model, - TauArgs &tau_args, - const double &tau_tol, - const double ¤t_time, - const double &save_time, - const std::vector &propensity_values, - const std::vector ¤t_state); + template double select( + Model &model, + TauArgs &tau_args, + const double &tau_tol, + const double ¤t_time, + const double &save_time, + const std::vector &propensity_values, + const std::vector ¤t_state); } diff --git a/gillespy2/solvers/cpp/c_base/Tau/tau.h b/gillespy2/solvers/cpp/c_base/Tau/tau.h index fe7f2fe7f..de3f5a54f 100644 --- a/gillespy2/solvers/cpp/c_base/Tau/tau.h +++ b/gillespy2/solvers/cpp/c_base/Tau/tau.h @@ -16,8 +16,7 @@ * along with this program. If not, see . */ -#ifndef TAU_H -#define TAU_H +#pragma once #include "model.h" @@ -27,7 +26,8 @@ #include -namespace Gillespy { +namespace Gillespy +{ template struct TauArgs @@ -81,4 +81,3 @@ namespace Gillespy { const std::vector &propensity_values, const std::vector ¤t_state); } -#endif //TAU_H \ No newline at end of file diff --git a/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/HybridModel.h b/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/HybridModel.h index 11a1d9801..04a1e16e2 100644 --- a/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/HybridModel.h +++ b/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/HybridModel.h @@ -25,7 +25,8 @@ #define GPY_HYBRID_ABSTOL 1e-5 #define GPY_HYBRID_RELTOL 1e-5 -namespace Gillespy::TauHybrid { +namespace Gillespy::TauHybrid +{ typedef int ReactionId; @@ -94,8 +95,8 @@ namespace Gillespy::TauHybrid { struct HybridSimulation : Simulation { - std::vector species_state; - std::vector reaction_state; + std::vector species_state; + std::vector reaction_state; HybridSimulation(); HybridSimulation(const Model &model); diff --git a/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/TauHybridSimulation.cpp b/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/TauHybridSimulation.cpp index ee93de63d..dae9f7d98 100644 --- a/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/TauHybridSimulation.cpp +++ b/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/TauHybridSimulation.cpp @@ -36,16 +36,18 @@ double end_time = 100.0; bool seed_time = true; double increment = 0; double tau_tol = 0.05; -class PropensityFunction : public IPropensityFunction{ + +class PropensityFunction : public IPropensityFunction +{ public: double ODEEvaluate(int reaction_number, const std::vector &S){ return map_ode_propensity(reaction_number, S); } - double TauEvaluate(unsigned int reaction_number, const std::vector &S) { + double TauEvaluate(unsigned int reaction_number, const std::vector &S) { return map_propensity(reaction_number, S); } - double evaluate(unsigned int reaction_number, unsigned int* S){return 1.0;} + double evaluate(unsigned int reaction_number, unsigned int* S){return 1.0;} }; double Gillespy::TauHybrid::HybridReaction::ode_propensity( @@ -62,7 +64,8 @@ double Gillespy::TauHybrid::HybridReaction::ssa_propensity( return map_propensity(reaction_number, state); } -int main(int argc, char* argv[]){ +int main(int argc, char* argv[]) +{ ArgParser parser(argc, argv); random_seed = parser.seed; seed_time = (random_seed == -1); diff --git a/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/TauHybridSolver.cpp b/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/TauHybridSolver.cpp index b445b50a5..62f9aa9ca 100644 --- a/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/TauHybridSolver.cpp +++ b/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/TauHybridSolver.cpp @@ -29,9 +29,9 @@ #include "HybridModel.h" #include "integrator.h" #include "tau.h" -using namespace Gillespy; -namespace Gillespy::TauHybrid { +namespace Gillespy::TauHybrid +{ bool interrupted = false; void signalHandler(int signum) @@ -41,7 +41,8 @@ namespace Gillespy::TauHybrid { void TauHybridCSolver(HybridSimulation *simulation, const double tau_tol) { - if (simulation == NULL) { + if (simulation == NULL) + { return; } @@ -55,13 +56,15 @@ namespace Gillespy::TauHybrid { // Tau selector initialization. Used to select a valid tau step. TauArgs tau_args = initialize(model, tau_tol); - //copy initial state for each trajectory - for(int s = 0; s < num_species; s++){ + // Copy initial state for each trajectory + for (int s = 0; s < num_species; s++) + { simulation->trajectories[0][0][s] = species[s].initial_population; } - //Simulate for each trajectory - for(int traj = 0; traj < num_trajectories; traj++){ + // Simulate for each trajectory + for (int traj = 0; traj < num_trajectories; traj++) + { // Population/concentration state values for each species. // TODO: change back double -> hybrid_state, once we figure out how that works std::vector current_state(num_species); @@ -74,7 +77,8 @@ namespace Gillespy::TauHybrid { Integrator sol(simulation, y0, GPY_HYBRID_RELTOL, GPY_HYBRID_ABSTOL); // Initialize the species population for the trajectory. - for (int spec_i = 0; spec_i < num_species; ++spec_i) { + for (int spec_i = 0; spec_i < num_species; ++spec_i) + { current_state[spec_i] = species[spec_i].initial_population; current_populations[spec_i] = species[spec_i].initial_population; } @@ -92,7 +96,8 @@ namespace Gillespy::TauHybrid { // Should be 0-initialized each time it's used. int *population_changes = new int[num_species]; simulation->current_time = 0; - while (simulation->current_time < simulation->end_time) { + while (simulation->current_time < simulation->end_time) + { // Expected tau step is determined. tau_step = select( model, @@ -136,25 +141,29 @@ namespace Gillespy::TauHybrid { IntegrationResults result = sol.integrate(&next_time); // 0-initialize our population_changes array. - for (int p_i = 0; p_i < num_species; ++p_i) { + for (int p_i = 0; p_i < num_species; ++p_i) + { population_changes[p_i] = 0; } // Start with the species concentration as a baseline value. // Stochastic reactions will update populations relative to their concentrations. - for (int spec_i = 0; spec_i < num_species; ++spec_i) { + for (int spec_i = 0; spec_i < num_species; ++spec_i) + { current_state[spec_i] = result.concentrations[spec_i]; } // The newly-updated reaction_states vector may need to be reconciled now. // A positive reaction_state means reactions have potentially fired. // NOTE: it is possible for a population to swing negative, where a smaller Tau is needed. - for (int rxn_i = 0; rxn_i < num_reactions; ++rxn_i) { + for (int rxn_i = 0; rxn_i < num_reactions; ++rxn_i) + { // Temporary variable for the reaction's state. // Does not get updated unless the changes are deemed valid. double rxn_state = result.reactions[rxn_i]; - switch (simulation->reaction_state[rxn_i].mode) { + switch (simulation->reaction_state[rxn_i].mode) + { case SimulationState::DISCRETE: while (rxn_state >= 0) { // "Fire" a reaction by recording changes in dependent species. @@ -180,12 +189,14 @@ namespace Gillespy::TauHybrid { // Positive reaction state means a negative population was detected. // Only update state with the given population changes if valid. - if (invalid_state) { + if (invalid_state) + { sol.restore_state(); tau_step /= 2; next_time = simulation->current_time + tau_step; } - else { + else + { // "Permanently" update the rxn_state and populations. for (int p_i = 0; p_i < num_species; ++p_i) { current_state[p_i] += population_changes[p_i]; @@ -201,8 +212,10 @@ namespace Gillespy::TauHybrid { // Seek forward, writing out any values on the timeline which are on current timestep range. // - while (save_idx < simulation->number_timesteps && save_time <= next_time) { - for (int spec_i = 0; spec_i < num_species; ++spec_i) { + while (save_idx < simulation->number_timesteps && save_time <= next_time) + { + for (int spec_i = 0; spec_i < num_species; ++spec_i) + { simulation->trajectories[traj][save_idx][spec_i] = current_state[spec_i]; } save_time = simulation->timeline[++save_idx]; diff --git a/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/TauHybridSolver.h b/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/TauHybridSolver.h index 2ff7d74d9..d45f3d7d7 100644 --- a/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/TauHybridSolver.h +++ b/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/TauHybridSolver.h @@ -16,12 +16,11 @@ * along with this program. If not, see . */ -#ifndef TAUHYBRIDCSOLVER_H -#define TAUHYBRIDCSOLVER_H +#pragma once + #include "HybridModel.h" -namespace Gillespy::TauHybrid { +namespace Gillespy::TauHybrid +{ void TauHybridCSolver(HybridSimulation* simulation, const double tau_tol); } - -#endif \ No newline at end of file From 5c79b45a3f71eee4bdf73f2791fafbd427cead0c Mon Sep 17 00:00:00 2001 From: Joshua Cooper Date: Tue, 6 Jul 2021 12:35:38 -0400 Subject: [PATCH 83/88] Fix for multi-trajectory simulations Specifying >1 for `number_of_trajectories` was resulting in constant output at best, integration errors at worst. - Supply simulation seed to prevent deterministic behavior - Back up `y0` and reset integrator completely on each trajectory - Explicitly initialize partition modes for each species --- .../tau_hybrid_cpp_solver/HybridModel.cpp | 6 +-- .../tau_hybrid_cpp_solver/HybridModel.h | 6 +-- .../tau_hybrid_cpp_solver/TauHybridSolver.cpp | 42 +++++++++++++------ .../tau_hybrid_cpp_solver/integrator.cpp | 42 +++++++++++++++---- .../c_base/tau_hybrid_cpp_solver/integrator.h | 2 + gillespy2/solvers/cpp/tau_hybrid_c_solver.py | 2 +- 6 files changed, 72 insertions(+), 28 deletions(-) diff --git a/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/HybridModel.cpp b/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/HybridModel.cpp index 932a7837a..00f1bd5c1 100644 --- a/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/HybridModel.cpp +++ b/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/HybridModel.cpp @@ -29,8 +29,8 @@ namespace Gillespy::TauHybrid } HybridSpecies::HybridSpecies() - : user_mode(SimulationState::CONTINUOUS), - partition_mode(SimulationState::CONTINUOUS), + : user_mode(SimulationState::DYNAMIC), + partition_mode(SimulationState::DISCRETE), switch_tol(0.03), switch_min(0) { @@ -258,7 +258,7 @@ namespace Gillespy::TauHybrid { for (int spec_i = 0; spec_i < species.size(); ++spec_i) { switch (species[spec_i].partition_mode) { - case SimulationState::CONTINUOUS: + case SimulationState::DISCRETE: current_state[spec_i] = std::floor(current_state[spec_i]); break; default: diff --git a/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/HybridModel.h b/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/HybridModel.h index 04a1e16e2..caf021d60 100644 --- a/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/HybridModel.h +++ b/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/HybridModel.h @@ -44,11 +44,11 @@ namespace Gillespy::TauHybrid double evaluate(std::vector &ode_state, std::vector &ssa_state); }; - enum SimulationState + enum SimulationState : unsigned int { CONTINUOUS = 0, - DISCRETE, - DYNAMIC + DISCRETE = 1, + DYNAMIC = 2 }; struct HybridSpecies diff --git a/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/TauHybridSolver.cpp b/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/TauHybridSolver.cpp index 62f9aa9ca..a6b16aca9 100644 --- a/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/TauHybridSolver.cpp +++ b/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/TauHybridSolver.cpp @@ -53,29 +53,47 @@ namespace Gillespy::TauHybrid std::unique_ptr[]> &species = model.species; double increment = simulation->timeline[1] - simulation->timeline[0]; - // Tau selector initialization. Used to select a valid tau step. - TauArgs tau_args = initialize(model, tau_tol); - - // Copy initial state for each trajectory - for (int s = 0; s < num_species; s++) + URNGenerator urn(simulation->random_seed); + // The contents of y0 are "stolen" by the integrator. + // Do not attempt to directly use y0 after being passed to sol! + N_Vector y0 = init_model_vector(model, urn); + N_Vector y; + if (num_trajectories > 0) + { + y = init_model_vector(model, urn); + } + else { - simulation->trajectories[0][0][s] = species[s].initial_population; + y = y0; } + Integrator sol(simulation, y, GPY_HYBRID_RELTOL, GPY_HYBRID_ABSTOL); + + // Tau selector initialization. Used to select a valid tau step. + TauArgs tau_args = initialize(model, tau_tol); // Simulate for each trajectory for (int traj = 0; traj < num_trajectories; traj++) { + if (traj > 0) + { + sol.reinitialize(y0); + } + + // Initialize each species with their respective user modes. + for (int spec_i = 0; spec_i < num_species; ++spec_i) + { + HybridSpecies *spec = &simulation->species_state[spec_i]; + spec->partition_mode = spec->user_mode == SimulationState::DYNAMIC + ? SimulationState::DISCRETE + : spec->user_mode; + simulation->trajectories[traj][0][spec_i] = spec->base_species->initial_population; + } + // Population/concentration state values for each species. // TODO: change back double -> hybrid_state, once we figure out how that works std::vector current_state(num_species); std::vector current_populations(num_species); - URNGenerator urn; - // The contents of y0 are "stolen" by the integrator. - // Do not attempt to directly use y0 after being passed to sol! - N_Vector y0 = init_model_vector(model, urn); - Integrator sol(simulation, y0, GPY_HYBRID_RELTOL, GPY_HYBRID_ABSTOL); - // Initialize the species population for the trajectory. for (int spec_i = 0; spec_i < num_species; ++spec_i) { diff --git a/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/integrator.cpp b/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/integrator.cpp index dc752a7bd..bcfb69319 100644 --- a/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/integrator.cpp +++ b/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/integrator.cpp @@ -54,12 +54,18 @@ Integrator::Integrator(HybridSimulation *simulation, N_Vector y0, double reltol, NV_Ith_S(y, mem_i) = NV_Ith_S(this->y0, mem_i); } - for (int spec_i = 0; spec_i < num_species; ++spec_i) { + for (int spec_i = 0; spec_i < num_species; ++spec_i) + { data.populations[spec_i] = data.concentrations[spec_i] = simulation->model->species[spec_i].initial_population; } + for (int rxn_i = 0; rxn_i < num_reactions; ++rxn_i) + { + data.propensities[rxn_i] = 0; + } + cvode_mem = CVodeCreate(CV_BDF); validate(CVodeInit(cvode_mem, rhs, t, y)); validate(CVodeSStolerances(cvode_mem, reltol, abstol)); @@ -72,7 +78,8 @@ Integrator::Integrator(HybridSimulation *simulation, N_Vector y0, double reltol, double Integrator::save_state() { int max_offset = num_reactions + num_species; - for (int mem_i = 0; mem_i < max_offset; ++mem_i) { + for (int mem_i = 0; mem_i < max_offset; ++mem_i) + { NV_Ith_S(y0, mem_i) = NV_Ith_S(y, mem_i); } @@ -83,7 +90,8 @@ double Integrator::save_state() double Integrator::restore_state() { int max_offset = num_reactions + num_species; - for (int mem_i = 0; mem_i < max_offset; ++mem_i) { + for (int mem_i = 0; mem_i < max_offset; ++mem_i) + { NV_Ith_S(y, mem_i) = NV_Ith_S(y0, mem_i); } validate(CVodeReInit(cvode_mem, t0, y0)); @@ -96,6 +104,16 @@ void Integrator::refresh_state() validate(CVodeReInit(cvode_mem, t, y)); } +void Integrator::reinitialize(N_Vector y_reset) +{ + int max_offset = num_reactions + num_species; + for (int mem_i = 0; mem_i < max_offset; ++mem_i) + { + NV_Ith_S(y0, mem_i) = NV_Ith_S(y_reset, mem_i); + } + validate(CVodeReInit(cvode_mem, 0, y0)); +} + Integrator::~Integrator() { N_VDestroy_Serial(y); @@ -105,7 +123,8 @@ Integrator::~Integrator() IntegrationResults Integrator::integrate(double *t) { - if (!validate(CVode(cvode_mem, *t, y, &this->t, CV_NORMAL))) { + if (!validate(CVode(cvode_mem, *t, y, &this->t, CV_NORMAL))) + { return { nullptr, nullptr }; } @@ -151,13 +170,15 @@ N_Vector Gillespy::TauHybrid::init_model_vector(Gillespy::Model &model, // The first half of the integration vector is used for integrating species concentrations. // [ --- concentrations --- | ... - for (int spec_i = 0; spec_i < model.number_species; ++spec_i) { + for (int spec_i = 0; spec_i < model.number_species; ++spec_i) + { NV_Ith_S(y0, spec_i) = model.species[spec_i].initial_population; } // The second half represents the current "randomized state" for each reaction. // ... | --- rxn_offsets --- ] - for (int rxn_i = model.number_species; rxn_i < rxn_offset_boundary; ++rxn_i) { + for (int rxn_i = model.number_species; rxn_i < rxn_offset_boundary; ++rxn_i) + { // Represents the current "randomized state" for each reaction, used as a // helper value to determine if/how many stochastic reactions fire. // This gets initialized to a random negative offset, and gets "less negative" @@ -202,7 +223,8 @@ int Gillespy::TauHybrid::rhs(realtype t, N_Vector y, N_Vector ydot, void *user_d // Populate the current ODE state into the concentrations vector. // dy/dt results are initialized to zero, and become the change in propensity. unsigned int spec_i; - for (spec_i = 0; spec_i < num_species; ++spec_i) { + for (spec_i = 0; spec_i < num_species; ++spec_i) + { concentrations[spec_i] = Y[spec_i]; populations[spec_i] = Y[spec_i]; } @@ -210,13 +232,15 @@ int Gillespy::TauHybrid::rhs(realtype t, N_Vector y, N_Vector ydot, void *user_d // Deterministic reactions generally are "evaluated" by generating dy/dt functions // for each of their dependent species. // To handle these, we will go ahead and evaluate each species' differential equations. - for (spec_i = 0; spec_i < num_species; ++spec_i) { + for (spec_i = 0; spec_i < num_species; ++spec_i) + { dydt[spec_i] = (*species)[spec_i].diff_equation.evaluate(concentrations, populations); } // Process deterministic propensity state // These updates get written directly to the integrator's concentration state - for (int rxn_i = 0; rxn_i < num_reactions; ++rxn_i) { + for (int rxn_i = 0; rxn_i < num_reactions; ++rxn_i) + { switch ((*reactions)[rxn_i].mode) { case SimulationState::DISCRETE: // Process stochastic reaction state by updating the root offset for each reaction. diff --git a/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/integrator.h b/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/integrator.h index 74e245316..162d9d6f8 100644 --- a/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/integrator.h +++ b/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/integrator.h @@ -95,6 +95,8 @@ namespace Gillespy::TauHybrid */ void refresh_state(); + void reinitialize(N_Vector y_reset); + IntegrationResults integrate(double *t); IntegratorData data; diff --git a/gillespy2/solvers/cpp/tau_hybrid_c_solver.py b/gillespy2/solvers/cpp/tau_hybrid_c_solver.py index 19522cc82..52c6b90f2 100644 --- a/gillespy2/solvers/cpp/tau_hybrid_c_solver.py +++ b/gillespy2/solvers/cpp/tau_hybrid_c_solver.py @@ -32,7 +32,7 @@ def __create_template_options(cls, species: "list[gillespy2.Species]"): species_mode_list = [] for spec_id, spec in enumerate(species): # Continuous by default - mode_keyword = species_mode_map.get(spec.mode, species_mode_map["continuous"]) + mode_keyword = species_mode_map.get(spec.mode, species_mode_map["dynamic"]) species_mode_list.append(f"SPECIES_MODE({spec_id},{mode_keyword},{spec.switch_min})") return { From 934a30ce9080fd92927f7473905badb01057b555 Mon Sep 17 00:00:00 2001 From: Joshua Cooper Date: Tue, 6 Jul 2021 12:45:48 -0400 Subject: [PATCH 84/88] Fix for continuous output on certain dynamic models Species would often get "stuck" on deterministic output. This was due to propensities not being initialized on the first run. - Add propensity computation to beginning of solver loop --- .../tau_hybrid_cpp_solver/TauHybridSolver.cpp | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/TauHybridSolver.cpp b/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/TauHybridSolver.cpp index a6b16aca9..f8c170003 100644 --- a/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/TauHybridSolver.cpp +++ b/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/TauHybridSolver.cpp @@ -116,6 +116,25 @@ namespace Gillespy::TauHybrid simulation->current_time = 0; while (simulation->current_time < simulation->end_time) { + // Compute current propensity values based on existing state. + for (int rxn_i = 0; rxn_i < num_reactions; ++rxn_i) + { + HybridReaction &rxn = simulation->reaction_state[rxn_i]; + double propensity = 0.0; + switch (rxn.mode) + { + case SimulationState::CONTINUOUS: + propensity = rxn.ode_propensity(rxn_i, current_state); + break; + case SimulationState::DISCRETE: + propensity = rxn.ssa_propensity(rxn_i, current_populations); + break; + default: + break; + } + sol.data.propensities[rxn_i] = propensity; + } + // Expected tau step is determined. tau_step = select( model, From a0c0a54f172876affbaad320b8680aead6438aa2 Mon Sep 17 00:00:00 2001 From: Joshua Cooper Date: Tue, 6 Jul 2021 16:10:12 -0400 Subject: [PATCH 85/88] Add error handling for CVODE methods - Implement `validate` function to set integrator status based on CVODE return code - Stop trajectory if the timestep is too small --- .../tau_hybrid_cpp_solver/TauHybridSolver.cpp | 30 ++++++++++--- .../tau_hybrid_cpp_solver/integrator.cpp | 42 +++++++++++++------ .../c_base/tau_hybrid_cpp_solver/integrator.h | 17 ++++++++ 3 files changed, 72 insertions(+), 17 deletions(-) diff --git a/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/TauHybridSolver.cpp b/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/TauHybridSolver.cpp index f8c170003..dc599cbbd 100644 --- a/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/TauHybridSolver.cpp +++ b/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/TauHybridSolver.cpp @@ -114,7 +114,12 @@ namespace Gillespy::TauHybrid // Should be 0-initialized each time it's used. int *population_changes = new int[num_species]; simulation->current_time = 0; - while (simulation->current_time < simulation->end_time) + + // An invalid simulation state indicates that an unrecoverable error has occurred, + // and the trajectory should terminate early. + bool invalid_state = false; + + while (!invalid_state && simulation->current_time < simulation->end_time) { // Compute current propensity values based on existing state. for (int rxn_i = 0; rxn_i < num_reactions; ++rxn_i) @@ -168,14 +173,22 @@ namespace Gillespy::TauHybrid // The integration loop continues until a valid solution is found. // Any invalid Tau steps (which cause negative populations) are discarded. sol.save_state(); - bool invalid_state; do { - invalid_state = false; // Integration Step // For deterministic reactions, the concentrations are updated directly. // For stochastic reactions, integration updates the rxn_offsets vector. - // flag = CVode(cvode_mem, next_time, y0, &next_time, CV_NORMAL); IntegrationResults result = sol.integrate(&next_time); + if (sol.status == IntegrationStatus::BAD_STEP_SIZE) + { + invalid_state = true; + // Breaking early causes `invalid_state` to remain set, + // resulting in an early termination of the trajectory. + break; + } + + // The integrator has, at this point, been validated. + // Any errors beyond this point is assumed to be a stochastic state failure. + invalid_state = false; // 0-initialize our population_changes array. for (int p_i = 0; p_i < num_species; ++p_i) @@ -243,12 +256,19 @@ namespace Gillespy::TauHybrid } } while (invalid_state); + // Invalid state after the do-while loop implies that an unrecoverable error has occurred. + // While prior results are considered usable, the current integration results are not. + // Calling `continue` with an invalid state will discard the results and terminate the trajectory. + if (invalid_state) + { + continue; + } + // Output the results for this time step. sol.refresh_state(); simulation->current_time = next_time; // Seek forward, writing out any values on the timeline which are on current timestep range. - // while (save_idx < simulation->number_timesteps && save_time <= next_time) { for (int spec_i = 0; spec_i < num_species; ++spec_i) diff --git a/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/integrator.cpp b/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/integrator.cpp index bcfb69319..17ae90389 100644 --- a/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/integrator.cpp +++ b/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/integrator.cpp @@ -18,10 +18,9 @@ #include "integrator.h" -static bool validate(int retcode); - using namespace Gillespy::TauHybrid; +static bool validate(Integrator *integrator, int retcode); IntegratorData::IntegratorData( HybridSimulation *simulation, @@ -67,12 +66,12 @@ Integrator::Integrator(HybridSimulation *simulation, N_Vector y0, double reltol, } cvode_mem = CVodeCreate(CV_BDF); - validate(CVodeInit(cvode_mem, rhs, t, y)); - validate(CVodeSStolerances(cvode_mem, reltol, abstol)); + validate(this, CVodeInit(cvode_mem, rhs, t, y)); + validate(this, CVodeSStolerances(cvode_mem, reltol, abstol)); solver = SUNLinSol_SPGMR(y, 0, 0); - validate(CVodeSetUserData(cvode_mem, &data)); - validate(CVodeSetLinearSolver(cvode_mem, solver, NULL)); + validate(this, CVodeSetUserData(cvode_mem, &data)); + validate(this, CVodeSetLinearSolver(cvode_mem, solver, NULL)); } double Integrator::save_state() @@ -94,14 +93,17 @@ double Integrator::restore_state() { NV_Ith_S(y, mem_i) = NV_Ith_S(y0, mem_i); } - validate(CVodeReInit(cvode_mem, t0, y0)); + if (!validate(this, CVodeReInit(cvode_mem, t0, y0))) + { + return 0; + } return t0; } void Integrator::refresh_state() { - validate(CVodeReInit(cvode_mem, t, y)); + validate(this, CVodeReInit(cvode_mem, t, y)); } void Integrator::reinitialize(N_Vector y_reset) @@ -111,7 +113,7 @@ void Integrator::reinitialize(N_Vector y_reset) { NV_Ith_S(y0, mem_i) = NV_Ith_S(y_reset, mem_i); } - validate(CVodeReInit(cvode_mem, 0, y0)); + validate(this, CVodeReInit(cvode_mem, 0, y0)); } Integrator::~Integrator() @@ -123,7 +125,7 @@ Integrator::~Integrator() IntegrationResults Integrator::integrate(double *t) { - if (!validate(CVode(cvode_mem, *t, y, &this->t, CV_NORMAL))) + if (!validate(this, CVode(cvode_mem, *t, y, &this->t, CV_NORMAL))) { return { nullptr, nullptr }; } @@ -260,7 +262,23 @@ int Gillespy::TauHybrid::rhs(realtype t, N_Vector y, N_Vector ydot, void *user_d }; -bool validate(int retcode) +static bool validate(Integrator *integrator, int retcode) { - return true; + switch (retcode) + { + case CV_MEM_NULL: + integrator->status = IntegrationStatus::NULL_POINTER; + return false; + case CV_NO_MALLOC: + integrator->status = IntegrationStatus::BAD_MEMORY; + return false; + case CV_TOO_CLOSE: + case CV_TOO_MUCH_WORK: + integrator->status = IntegrationStatus::BAD_STEP_SIZE; + return false; + case CV_SUCCESS: + default: + integrator->status = IntegrationStatus::OK; + return true; + } } diff --git a/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/integrator.h b/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/integrator.h index 162d9d6f8..542eb6a89 100644 --- a/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/integrator.h +++ b/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/integrator.h @@ -29,6 +29,21 @@ namespace Gillespy::TauHybrid { + /* IntegratorStatus: represents the runtime state of the integrator. + * OK indicates that no errors have occurred. + */ + enum IntegrationStatus + { + // No errors have occurred. + OK = 0, + // Attempted to perform a SUNDIALS operation on a null CVODE object. + NULL_POINTER, + // A non-null object resulted in a memory error and must be initialized. + BAD_MEMORY, + // Could not perform integration, step size too small. + BAD_STEP_SIZE + }; + struct IntegratorData { HybridSimulation *simulation; @@ -70,6 +85,8 @@ namespace Gillespy::TauHybrid int num_species; int num_reactions; public: + // status: check for errors before using the results. + IntegrationStatus status; N_Vector y; realtype t; From a86935be924ecc56b11d5168e6650fc91136be2e Mon Sep 17 00:00:00 2001 From: Joshua Cooper Date: Thu, 8 Jul 2021 14:46:25 -0400 Subject: [PATCH 86/88] Update ODE solver's initial populations to use `double` - Fixes issue where fractional initial populations have unexpected results (i.e. Lotka-Volterra model) --- gillespy2/solvers/cpp/c_base/ode_cpp_solver/ODESolver.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gillespy2/solvers/cpp/c_base/ode_cpp_solver/ODESolver.cpp b/gillespy2/solvers/cpp/c_base/ode_cpp_solver/ODESolver.cpp index 14423312e..51758e76a 100644 --- a/gillespy2/solvers/cpp/c_base/ode_cpp_solver/ODESolver.cpp +++ b/gillespy2/solvers/cpp/c_base/ode_cpp_solver/ODESolver.cpp @@ -65,7 +65,7 @@ namespace Gillespy // Add species initial conditions to the current state vectory `y0`. for (unsigned int species_index = 0; species_index < simulation_model->number_species; species_index++) { - unsigned int initial_population = simulation_model->species[species_index].initial_population; + double initial_population = simulation_model->species[species_index].initial_population; NV_Ith_S(y0, species_index) = initial_population; simulation->trajectories[0][0][species_index] = initial_population; From 9487a454f10e11b868d7a1460de67f1cbad69fae Mon Sep 17 00:00:00 2001 From: Joshua Cooper Date: Wed, 14 Jul 2021 14:52:22 -0400 Subject: [PATCH 87/88] Bug fixes in integrator - Revert `SimulationState` enum properties to not use bit packing - Fix incorrect computation of standard deviation, which caused unexpected output - Fix CVODE error caused by uninitialized time value --- .../cpp/c_base/tau_hybrid_cpp_solver/HybridModel.cpp | 4 ++-- .../solvers/cpp/c_base/tau_hybrid_cpp_solver/HybridModel.h | 6 +++--- .../cpp/c_base/tau_hybrid_cpp_solver/TauHybridSolver.cpp | 5 +---- .../solvers/cpp/c_base/tau_hybrid_cpp_solver/integrator.cpp | 4 +++- .../solvers/cpp/c_base/tau_hybrid_cpp_solver/integrator.h | 2 +- 5 files changed, 10 insertions(+), 11 deletions(-) diff --git a/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/HybridModel.cpp b/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/HybridModel.cpp index 00f1bd5c1..0ff397d7b 100644 --- a/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/HybridModel.cpp +++ b/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/HybridModel.cpp @@ -211,13 +211,13 @@ namespace Gillespy::TauHybrid if (spec_dx < 0) { // Selected species is a reactant. means[spec_i] -= (tau_step * propensity_values[rxn_i] * spec_dx); - sd[spec_i] += std::pow((tau_step * propensity_values[rxn_i] * spec_dx), 2); + sd[spec_i] += (tau_step * propensity_values[rxn_i] * std::pow(spec_dx, 2)); } else if (spec_dx > 0) { // Selected species is a product. HybridSpecies &product = species[spec_i]; means[spec_i] += (tau_step * propensity_values[rxn_i] * spec_dx); - sd[spec_i] += std::pow((tau_step * propensity_values[rxn_i] * spec_dx), 2); + sd[spec_i] += (tau_step * propensity_values[rxn_i] * std::pow(spec_dx, 2)); } } } diff --git a/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/HybridModel.h b/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/HybridModel.h index caf021d60..c4c33428f 100644 --- a/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/HybridModel.h +++ b/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/HybridModel.h @@ -60,13 +60,13 @@ namespace Gillespy::TauHybrid // CONTINUOUS or DISCRETE // otherwise, mode will be determined by the program (DYNAMIC) // if no choice is made, DYNAMIC will be assumed - SimulationState user_mode : 2; + SimulationState user_mode; // during simulation execution, a species will fall into either of the two categories, CONTINUOUS or DISCRETE // this is pre-determined only if the user_mode specifies CONTINUOUS or DISCRETE. // otherwise, if DYNAMIC is specified, partition_mode will be continually calculated throughout the simulation // according to standard deviation and coefficient of variance. - SimulationState partition_mode : 1; + SimulationState partition_mode; // Tolerance level for considering a dynamic species deterministically, value is compared // to an estimated sd/mean population of a species after a given time step. @@ -85,7 +85,7 @@ namespace Gillespy::TauHybrid struct HybridReaction { Reaction *base_reaction; - SimulationState mode : 1; + SimulationState mode; HybridReaction(); diff --git a/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/TauHybridSolver.cpp b/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/TauHybridSolver.cpp index dc599cbbd..f0e65f3bc 100644 --- a/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/TauHybridSolver.cpp +++ b/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/TauHybridSolver.cpp @@ -101,9 +101,6 @@ namespace Gillespy::TauHybrid current_populations[spec_i] = species[spec_i].initial_population; } - // Represents the largest valid index of the output vector(s), y (and y0). - int rxn_offset_boundary = num_species + num_reactions; - // SIMULATION STEP LOOP int save_idx = 1; double next_time; @@ -250,7 +247,7 @@ namespace Gillespy::TauHybrid // "Permanently" update the rxn_state and populations. for (int p_i = 0; p_i < num_species; ++p_i) { current_state[p_i] += population_changes[p_i]; - current_populations[p_i] = current_state[p_i]; + current_populations[p_i] = (int) current_state[p_i]; result.concentrations[p_i] = current_state[p_i]; } } diff --git a/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/integrator.cpp b/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/integrator.cpp index 17ae90389..c52273c2f 100644 --- a/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/integrator.cpp +++ b/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/integrator.cpp @@ -42,6 +42,8 @@ IntegratorData::IntegratorData(HybridSimulation *simulation) Integrator::Integrator(HybridSimulation *simulation, N_Vector y0, double reltol, double abstol) : y0(y0), + t(0.0f), + t0(0.0f), y(N_VClone_Serial(y0)), data(simulation), num_reactions(simulation->model->number_reactions), @@ -140,7 +142,7 @@ IntegrationResults Integrator::integrate(double *t) URNGenerator::URNGenerator() : uniform(0, 1) {} -URNGenerator::URNGenerator(double seed) +URNGenerator::URNGenerator(unsigned long long seed) : uniform(0, 1), rng(seed) {} diff --git a/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/integrator.h b/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/integrator.h index 542eb6a89..73de75ab7 100644 --- a/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/integrator.h +++ b/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/integrator.h @@ -129,7 +129,7 @@ namespace Gillespy::TauHybrid public: double next(); URNGenerator(); - URNGenerator(double seed); + explicit URNGenerator(unsigned long long seed); }; N_Vector init_model_vector(Model &model, URNGenerator urn); From 49710b76ccc344c6c97765d6d7b435531d777808 Mon Sep 17 00:00:00 2001 From: Joshua Cooper Date: Thu, 15 Jul 2021 11:07:03 -0400 Subject: [PATCH 88/88] Add integrator guard to prevent infinite loops - Limits the number of sequential invalid states from exceeding 1000 - Prevents "occasionally invalid" output from terminating the simulation - Allow `tau_tol` to be properly accepted as argument --- gillespy2/solvers/cpp/c_base/arg_parser.h | 2 +- .../TauHybridSimulation.cpp | 2 +- .../tau_hybrid_cpp_solver/TauHybridSolver.cpp | 20 ++++++++++++++----- .../tau_hybrid_cpp_solver/integrator.cpp | 5 ++++- .../c_base/tau_hybrid_cpp_solver/integrator.h | 1 + 5 files changed, 22 insertions(+), 8 deletions(-) diff --git a/gillespy2/solvers/cpp/c_base/arg_parser.h b/gillespy2/solvers/cpp/c_base/arg_parser.h index 796b5d48f..09cf4c4e8 100644 --- a/gillespy2/solvers/cpp/c_base/arg_parser.h +++ b/gillespy2/solvers/cpp/c_base/arg_parser.h @@ -44,7 +44,7 @@ class ArgParser double end = 0.0; double increment = 0.0; double switch_tol = 0.0; - double tau_tol = 0.0; + double tau_tol = 0.03; ArgParser(int argc, char *argv[]); ~ArgParser(); diff --git a/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/TauHybridSimulation.cpp b/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/TauHybridSimulation.cpp index dae9f7d98..6192eb99d 100644 --- a/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/TauHybridSimulation.cpp +++ b/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/TauHybridSimulation.cpp @@ -35,7 +35,7 @@ int random_seed = 0; double end_time = 100.0; bool seed_time = true; double increment = 0; -double tau_tol = 0.05; +double tau_tol = 0.03; class PropensityFunction : public IPropensityFunction { diff --git a/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/TauHybridSolver.cpp b/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/TauHybridSolver.cpp index f0e65f3bc..925f917fd 100644 --- a/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/TauHybridSolver.cpp +++ b/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/TauHybridSolver.cpp @@ -115,8 +115,11 @@ namespace Gillespy::TauHybrid // An invalid simulation state indicates that an unrecoverable error has occurred, // and the trajectory should terminate early. bool invalid_state = false; + // This is a temporary fix. Ideally, invalid state should allow for integrator options change. + // For now, a "guard" is put in place to prevent potentially infinite loops from occurring. + unsigned int integration_guard = 1000; - while (!invalid_state && simulation->current_time < simulation->end_time) + while (integration_guard > 0 && simulation->current_time < simulation->end_time) { // Compute current propensity values based on existing state. for (int rxn_i = 0; rxn_i < num_reactions; ++rxn_i) @@ -256,10 +259,9 @@ namespace Gillespy::TauHybrid // Invalid state after the do-while loop implies that an unrecoverable error has occurred. // While prior results are considered usable, the current integration results are not. // Calling `continue` with an invalid state will discard the results and terminate the trajectory. - if (invalid_state) - { - continue; - } + integration_guard = invalid_state + ? integration_guard - 1 + : 1000; // Output the results for this time step. sol.refresh_state(); @@ -276,6 +278,14 @@ namespace Gillespy::TauHybrid } } + if (integration_guard == 0) + { + std::cerr + << "[Trajectory #" << traj << "] " + << "Integration guard triggered; problem space too stiff at t=" + << simulation->current_time << std::endl; + } + // End of trajectory delete[] population_changes; } diff --git a/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/integrator.cpp b/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/integrator.cpp index c52273c2f..f37204f0f 100644 --- a/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/integrator.cpp +++ b/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/integrator.cpp @@ -144,7 +144,10 @@ URNGenerator::URNGenerator() URNGenerator::URNGenerator(unsigned long long seed) : uniform(0, 1), - rng(seed) {} + rng(seed) +{ + this->seed = seed; +} /* Generate a new random floating-point number on the range [0,1). diff --git a/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/integrator.h b/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/integrator.h index 73de75ab7..968bd582c 100644 --- a/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/integrator.h +++ b/gillespy2/solvers/cpp/c_base/tau_hybrid_cpp_solver/integrator.h @@ -126,6 +126,7 @@ namespace Gillespy::TauHybrid private: std::uniform_real_distribution uniform; std::mt19937_64 rng; + unsigned long long seed; public: double next(); URNGenerator();