diff --git a/gillespy2/__init__.py b/gillespy2/__init__.py index 434b4f223..acec92ce7 100644 --- a/gillespy2/__init__.py +++ b/gillespy2/__init__.py @@ -3,13 +3,12 @@ """ import sys -if (sys.version_info < (3,0)): +if sys.version_info < (3, 0): raise Exception("GillesPy2 only works in Python 3.0 and higher") from .__version__ import __version__, __title__, __description__, __url__ from .__version__ import __author__, __email__ from .__version__ import __license__, __copyright__ - from gillespy2.solvers import * from gillespy2.core import * import gillespy2.sbml diff --git a/gillespy2/__version__.py b/gillespy2/__version__.py index 016f3eec3..b30dce690 100644 --- a/gillespy2/__version__.py +++ b/gillespy2/__version__.py @@ -5,7 +5,7 @@ # @website https://github.com/GillesPy2/GillesPy2 # ============================================================================= -__version__ = '1.5.0' +__version__ = '1.5.1' __title__ = 'GillesPy2' __description__ = 'Python interface for Gillespie-style biochemical simulations' __url__ = 'https://github.com/GillesPy2/GillesPy2' diff --git a/gillespy2/core/model.py b/gillespy2/core/model.py index 168c62c50..36920d842 100644 --- a/gillespy2/core/model.py +++ b/gillespy2/core/model.py @@ -702,7 +702,7 @@ def get_element(self, ename): return 'Element not found!' - def get_best_solver(self, precompile=True): + def get_best_solver(self, cpp_test = True): """ Finds best solver for the users simulation. Currently, AssignmentRules, RateRules, FunctionDefinitions, Events, and Species with a dynamic, or continuous population must use the TauHybridSolver. @@ -732,14 +732,18 @@ def get_best_solver(self, precompile=True): raise ModelError('TauHybridSolver is the only solver currently that supports ' 'AssignmentRules, RateRules, FunctionDefinitions, or Events. ' 'Please install Numpy.') + from gillespy2.solvers.utilities.solverutils import check_cpp_support + cpp_test = check_cpp_support() + + if cpp_test is False and can_use_numpy and not hybrid_check: + from gillespy2 import NumPySSASolver + return NumPySSASolver + else: - if precompile: - from gillespy2.solvers.cpp.variable_ssa_c_solver import VariableSSACSolver - return VariableSSACSolver - from gillespy2.solvers.auto import SSASolver - return SSASolver + from gillespy2 import VariableSSACSolver + return VariableSSACSolver - def run(self, solver=None, timeout=0, t=None, show_labels=True, **solver_args): + def run(self, solver=None, timeout=0, t=None, show_labels=True, cpp_support=False, **solver_args): """ Function calling simulation of the model. There are a number of parameters to be set here. @@ -768,16 +772,25 @@ def run(self, solver=None, timeout=0, t=None, show_labels=True, **solver_args): if not show_labels: from gillespy2.core import log - log.warning('show_labels = False is deprecated. Future releases of GillesPy2 may not support this feature.') - + log.warning('show_labels = False is deprecated. Future releases ' + 'of GillesPy2 may not support this feature.') if t is None: t = self.tspan[-1] + if solver is None: solver = self.get_best_solver() + try: solver_results, rc = solver.run(model=self, t=t, increment=self.tspan[-1] - self.tspan[-2], timeout=timeout, **solver_args) except Exception as e: + if cpp_support is False: + if not isinstance(solver, str): + if solver.name == 'SSACSolver' or solver.name == 'VariableSSACSolver': + from gillespy2.core import log + log.warning("Please install/configure 'g++' and 'make' on your" + " system, to ensure that GillesPy2 C solvers will" + " run properly.") raise SimulationError( "argument 'solver={}' to run() failed. Reason Given: {}".format(solver, e)) diff --git a/gillespy2/solvers/__init__.py b/gillespy2/solvers/__init__.py index 11a76e037..c771c03e7 100644 --- a/gillespy2/solvers/__init__.py +++ b/gillespy2/solvers/__init__.py @@ -1,6 +1,11 @@ +from gillespy2.core.gillespyError import ExecutionError from .cpp import * from .numpy import * +__all__ = cpp.__all__ + numpy.__all__ -__all__ = ["SSACSolver", "VariableSSACSolver", "NumPySSASolver", "ODESolver", "TauHybridSolver", "TauLeapingSolver"] - +if not __all__: + raise ExecutionError("Your computer does not contain the minimum require" + "ments for running simulations using GillesPy2." + " Please install NumPy, or configure the C++ 'g++' " + "compiler on your machine.") diff --git a/gillespy2/solvers/auto/__init__.py b/gillespy2/solvers/auto/__init__.py deleted file mode 100644 index 49c71772f..000000000 --- a/gillespy2/solvers/auto/__init__.py +++ /dev/null @@ -1,2 +0,0 @@ -from gillespy2.solvers.auto.ssa_solver import get_best_ssa_solver, SSASolver -__all__ = ['SSASolver', 'get_best_ssa_solver'] \ No newline at end of file diff --git a/gillespy2/solvers/auto/ssa_solver.py b/gillespy2/solvers/auto/ssa_solver.py deleted file mode 100644 index d4a6209a7..000000000 --- a/gillespy2/solvers/auto/ssa_solver.py +++ /dev/null @@ -1,32 +0,0 @@ -from gillespy2.core import log - -def get_best_ssa_solver(omit_cpp=False, omit_cython=True, omit_numpy=False): - if not omit_cpp: - from gillespy2.solvers.cpp import can_use_cpp - if can_use_cpp: - from gillespy2.solvers.cpp import SSACSolver - log.debug("Successful Import of SSACSolver.") - return SSACSolver - - if not omit_cython: - from gillespy2.solvers.cython import can_use_cython - if can_use_cython: - from gillespy2.solvers.cython import CythonSSASolver - log.debug("Successful Import of CythonSSASolver.") - return CythonSSASolver - - if not omit_numpy: - from gillespy2.solvers.numpy import can_use_numpy - if can_use_numpy: - from gillespy2.solvers.numpy.ssa_solver import NumPySSASolver - log.debug("Successful Import of NumPySSASolver.") - return NumPySSASolver - - log.warn('Minimum software requirements not met. Please install Numpy.') - return None - - -SSASolver = get_best_ssa_solver() - - -__all__ = ['SSASolver', 'get_best_ssa_solver'] diff --git a/gillespy2/solvers/cpp/__init__.py b/gillespy2/solvers/cpp/__init__.py index d4ef37dfb..9e4da3365 100644 --- a/gillespy2/solvers/cpp/__init__.py +++ b/gillespy2/solvers/cpp/__init__.py @@ -2,18 +2,5 @@ from gillespy2.solvers.cpp.variable_ssa_c_solver import VariableSSACSolver from gillespy2.core import log -def check_cpp_support(): - from gillespy2.solvers.cpp.example_models import Example - try: - model = Example() - results = model.run(solver=SSACSolver) - return True - except Exception as e: - log.warn('Unable to use C++ optimized SSA: {0}. The performance of ' \ - 'this package can be significantly increased if you install/configure GCC on ' \ - 'this machine.'.format(e)) - return False -can_use_cpp = check_cpp_support() - -__all__ = ['SSACSolver','VariableSSACSolver'] if can_use_cpp else [] +__all__ = ['SSACSolver', 'VariableSSACSolver'] diff --git a/gillespy2/solvers/numpy/ssa_solver.py b/gillespy2/solvers/numpy/ssa_solver.py index bf16f3092..a6b765912 100644 --- a/gillespy2/solvers/numpy/ssa_solver.py +++ b/gillespy2/solvers/numpy/ssa_solver.py @@ -56,7 +56,6 @@ def run(self, model, t=20, number_of_trajectories=1, increment=0.05, seed=None, if timeout is not None and timeout <= 0: timeout = None - if len(kwargs) > 0: for key in kwargs: log.warning('Unsupported keyword argument to {0} solver: {1}'.format(self.name, key)) @@ -77,13 +76,14 @@ def run(self, model, t=20, number_of_trajectories=1, increment=0.05, seed=None, # curr_time and curr_state are list of len 1 so that __run receives reference if resume is not None: - curr_time = [resume['time'][-1]] + total_time = [resume['time'][-1]] else: - curr_time = [0] + total_time = [0] + curr_state = [None] live_grapher = [None] - sim_thread = Thread(target=self.___run, args=(model, curr_state, curr_time, timeline, trajectory_base, + sim_thread = Thread(target=self.___run, args=(model, curr_state, total_time, timeline, trajectory_base, live_grapher,), kwargs={'t': t, 'number_of_trajectories': number_of_trajectories, 'increment': increment, @@ -105,7 +105,7 @@ def run(self, model, t=20, number_of_trajectories=1, increment=0.05, seed=None, live_output_options,resume = resumeTest) display_timer = gillespy2.core.liveGraphing.RepeatTimer(live_output_options['interval'], live_grapher[0].display, args=(curr_state, - curr_time, + total_time, trajectory_base,) ) display_timer.start() @@ -126,19 +126,19 @@ def run(self, model, t=20, number_of_trajectories=1, increment=0.05, seed=None, return self.result, self.rc - def ___run(self, model, curr_state, curr_time, timeline, trajectory_base, live_grapher, t=20, + def ___run(self, model, curr_state, total_time, timeline, trajectory_base, live_grapher, t=20, number_of_trajectories=1, increment=0.05, seed=None, debug=False, show_labels=True, resume=None, timeout=None): try: - self.__run(model, curr_state, curr_time, timeline, trajectory_base, live_grapher, t, number_of_trajectories, + self.__run(model, curr_state, total_time, timeline, trajectory_base, live_grapher, t, number_of_trajectories, increment, seed, debug, show_labels, resume, timeout) except Exception as e: self.has_raised_exception = e self.result = [] return [], -1 - def __run(self, model, curr_state, curr_time, timeline, trajectory_base, live_grapher, t=20, + def __run(self, model, curr_state, total_time, timeline, trajectory_base, live_grapher, t=20, number_of_trajectories=1, increment=0.05, seed=None, debug=False, show_labels=True, resume=None, timeout=None): @@ -191,6 +191,7 @@ def __run(self, model, curr_state, curr_time, timeline, trajectory_base, live_gr # begin simulating each trajectory simulation_data = [] for trajectory_num in range(number_of_trajectories): + total_time[0] = 0 if self.stop_event.is_set(): self.rc = 33 break @@ -206,6 +207,11 @@ def __run(self, model, curr_state, curr_time, timeline, trajectory_base, live_gr trajectory = trajectory_base[trajectory_num] entry_count = 1 curr_state[0] = {} + # curr_time and curr_state are list of len 1 so that __run receives reference + if resume is not None: + curr_time = [resume['time'][-1]] + else: + curr_time = [0] for spec in model.listOfSpecies: if resume is not None: @@ -242,6 +248,7 @@ def __run(self, model, curr_state, curr_time, timeline, trajectory_base, live_gr cumulative_sum = random.uniform(0, propensity_sum) curr_time[0] += -math.log(random.random()) / propensity_sum + total_time[0] += -math.log(random.random()) / propensity_sum if debug: print('cumulative sum: ', cumulative_sum) print('entry count: ', entry_count) diff --git a/gillespy2/solvers/numpy/tau_leaping_solver.py b/gillespy2/solvers/numpy/tau_leaping_solver.py index f692cf425..2ad4d7515 100644 --- a/gillespy2/solvers/numpy/tau_leaping_solver.py +++ b/gillespy2/solvers/numpy/tau_leaping_solver.py @@ -129,17 +129,16 @@ def run(self, model, t=20, number_of_trajectories=1, increment=0.05, seed=None, trajectory_base, tempSpecies = nputils.numpy_trajectory_base_initialization(model, number_of_trajectories, timeline, species, resume=resume ) - # curr_time and curr_state are list of len 1 so that __run receives reference if resume is not None: - curr_time = [resume['time'][-1]] + total_time = [resume['time'][-1]] else: - curr_time = [0] + total_time = [0] curr_state = [None] live_grapher = [None] - sim_thread = Thread(target=self.___run, args=(model, curr_state, curr_time, timeline, trajectory_base, + sim_thread = Thread(target=self.___run, args=(model, curr_state, total_time, timeline, trajectory_base, live_grapher,), kwargs={'t': t, 'number_of_trajectories': number_of_trajectories, @@ -161,7 +160,7 @@ def run(self, model, t=20, number_of_trajectories=1, increment=0.05, seed=None, live_grapher[0] = liveGraphing.LiveDisplayer(model, timeline, number_of_trajectories, live_output_options, resume=resumeTest) display_timer = liveGraphing.RepeatTimer(live_output_options['interval'], live_grapher[0].display, - args=(curr_state, curr_time, trajectory_base,)) + args=(curr_state, total_time, trajectory_base,)) display_timer.start() sim_thread.join(timeout=timeout) @@ -181,12 +180,12 @@ def run(self, model, t=20, number_of_trajectories=1, increment=0.05, seed=None, raise self.has_raised_exception return self.result, self.rc - def ___run(self, model, curr_state,curr_time, timeline, trajectory_base, live_grapher, t=20, + def ___run(self, model, curr_state,total_time, timeline, trajectory_base, live_grapher, t=20, number_of_trajectories=1, increment=0.05, seed=None, debug=False, profile=False, show_labels=True, timeout=None, resume=None, tau_tol=0.03, **kwargs): try: - self.__run(model, curr_state, curr_time, timeline, trajectory_base, live_grapher, t, number_of_trajectories, + self.__run(model, curr_state, total_time, timeline, trajectory_base, live_grapher, t, number_of_trajectories, increment, seed, debug, profile, timeout, resume, tau_tol, **kwargs) except Exception as e: @@ -194,7 +193,7 @@ def ___run(self, model, curr_state,curr_time, timeline, trajectory_base, live_gr self.result = [] return [], -1 - def __run(self, model, curr_state, curr_time, timeline, trajectory_base, live_grapher, t=20, + def __run(self, model, curr_state, total_time, timeline, trajectory_base, live_grapher, t=20, number_of_trajectories=1, increment=0.05, seed=None, debug=False, profile=False, timeout=None, resume=None, tau_tol=0.03, **kwargs): @@ -240,10 +239,13 @@ def __run(self, model, curr_state, curr_time, timeline, trajectory_base, live_gr start_state = [0] * (len(model.listOfReactions) + len(model.listOfRateRules)) propensities = {} curr_state[0] = {} + if resume is not None: - save_time = curr_time[0] + save_time = total_time[0] + curr_time = [save_time] else: save_time = 0 + curr_time = [0] curr_state[0]['vol'] = model.volume data = { 'time': timeline} @@ -345,6 +347,7 @@ def __run(self, model, curr_state, curr_time, timeline, trajectory_base, live_gr start_state = prev_start_state.copy() curr_state[0] = prev_curr_state.copy() curr_time[0] = prev_curr_time + total_time[0] = prev_curr_time tau_step = tau_step / 2 steps_rejected += 1 if debug: diff --git a/gillespy2/solvers/utilities/solverutils.py b/gillespy2/solvers/utilities/solverutils.py index 6800c21d8..bfa5a9bfc 100644 --- a/gillespy2/solvers/utilities/solverutils.py +++ b/gillespy2/solvers/utilities/solverutils.py @@ -282,5 +282,21 @@ def dependency_grapher(model, reactions): return dependent_rxns +""" +Below is a utility that checks whether or not users can use C++ solvers. +""" + +def check_cpp_support(): + from gillespy2.solvers.cpp.example_models import Example + from gillespy2 import SSACSolver + try: + model = Example() + results = model.run(solver=SSACSolver, cpp_support=True) + return True + except Exception as e: + log.warn('Unable to use C++ optimized SSA: {0}. The performance of ' \ + 'this package can be significantly increased if you install/configure GCC on ' \ + 'this machine.'.format(e)) + return False