def test_to_csv_single_result_no_stamp(self): test_data = {'time': [0]} result = Results(data=[test_data]) test_nametag = "test_nametag" with tempfile.TemporaryDirectory() as tempdir: result.to_csv(nametag=test_nametag, path=tempdir) assert len(os.listdir(tempdir)) is not 0
def test_to_csv_single_result_no_data(self): result = Results(data=None) test_nametag = "test_nametag" test_stamp = "test_stamp" with tempfile.TemporaryDirectory() as tempdir: result.to_csv(stamp=test_stamp, nametag=test_nametag, path=tempdir) test_path = tempdir + "/" + test_nametag + test_stamp assert not os.path.isdir(test_path)
def test_to_csv_single_result_no_nametag(self): test_model = Model('test_model') test_data = Trajectory(data={'time': [0]}, model=test_model) result = Results(data=[test_data]) result.solver_name = 'test_solver' test_stamp = "test_stamp" with tempfile.TemporaryDirectory() as tempdir: result.to_csv(stamp=test_stamp, path=tempdir) assert len(os.listdir(tempdir)) is not 0
def test_to_csv_single_result_file_exists(self): test_data = {'time': [0]} result = Results(data=[test_data]) test_nametag = "test_nametag" test_stamp = "test_stamp" with tempfile.TemporaryDirectory() as tempdir: result.to_csv(stamp=test_stamp, nametag=test_nametag, path=tempdir) test_path = tempdir + "/" + test_nametag + test_stamp + "/" + test_nametag + "0.csv" assert os.path.isfile(test_path)
def test_xscale_plot(self): from unittest import mock trajectory = Trajectory(data={ 'time': [0.], 'foo': [1.] }, model=Model('test_model')) results = Results(data=[trajectory]) with mock.patch('matplotlib.pyplot.xscale') as mock_xscale: results.plot(xscale='log') mock_xscale.assert_called_with('log')
def test_to_csv_single_result_no_path(self): test_data = Trajectory({'time': [0]}, model=Model('test_model'), solver_name='test_solver_name') result = Results(data=[test_data]) test_nametag = "test_nametag" test_stamp = "test_stamp"
def test_plotly_layout_args(self): from unittest import mock trajectory = Trajectory(data={ 'time': [0.], 'foo': [1.] }, model=Model('test_model')) results = Results(data=[trajectory]) with mock.patch('plotly.offline.init_notebook_mode') as mock_notebook: with mock.patch('plotly.graph_objs.Scatter') as mock_scatter: with mock.patch('plotly.graph_objs.Layout') as mock_layout: results.plotplotly(return_plotly_figure=True, xscale='log') mock_layout.assert_called_with(showlegend=True, title='', xaxis_title='Time ', xscale='log', yaxis_title='Species Population')
def test_plotly_std_dev_range_layout_args(self): from unittest import mock from unittest.mock import MagicMock trajectory = Trajectory(data={ 'time': [0.], 'foo': [1.] }, model=Model('test_model')) results = Results(data=[trajectory]) _plotly_iterate = MagicMock() with mock.patch('plotly.offline.init_notebook_mode') as mock_notebook: with mock.patch('plotly.graph_objs.Scatter') as mock_scatter: with mock.patch('plotly.graph_objs.Layout') as mock_layout: results.plotplotly_std_dev_range(return_plotly_figure=True, xscale='log') mock_layout.assert_called_with(legend={'traceorder': 'normal'}, showlegend=True, title='Standard Deviation Range', xaxis_title='Time ', xscale='log', yaxis_title='Species Population')
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. :param solver: The solver by which to simulate the model. This solver object may be initialized separately to specify an algorithm. Optional, defaults to ssa solver. :type solver: gillespy.GillesPySolver :param timeout: Allows a time_out value in seconds to be sent to a signal handler, restricting simulation run-time :type timeout: int :param t: End time of simulation :type t: int :param solver_args: Solver-specific arguments to be passed to solver.run() :return If show_labels is False, returns a numpy array of arrays of species population data. If show_labels is True,returns a Results object that inherits UserList and contains one or more Trajectory objects that inherit UserDict. Results object supports graphing and csv export. To pause a simulation and retrieve data before the simulation, keyboard interrupt the simulation by pressing control+c or pressing stop on a jupyter notebook. To resume a simulation, pass your previously ran results into the run method, and set t = to the time you wish the resuming simulation to end (run(resume=results, t=x)). Pause/Resume is only supported for SINGLE TRAJECTORY simulations. T MUST BE SET OR UNEXPECTED BEHAVIOR MAY OCCUR. """ 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.') 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)) if rc == 33: from gillespy2.core import log log.warning('GillesPy2 simulation exceeded timeout.') if hasattr(solver_results[0], 'shape'): return solver_results if len(solver_results) > 0: results_list = [] for i in range(0, len(solver_results)): temp = Trajectory(data=solver_results[i], model=self, solver_name=solver.name, rc=rc) results_list.append(temp) results = Results(results_list) if show_labels == False: results = results.to_array() return results if len(solver_results) > 0: results_list = [] for i in range(0, len(solver_results)): temp = Trajectory(data=solver_results[i], model=self, solver_name=solver.name, rc=rc) results_list.append(temp) results = Results(results_list) if show_labels == False: results = results.to_array() return results else: raise ValueError( "number_of_trajectories must be non-negative and non-zero")
def run(self=None, model=None, t=None, number_of_trajectories=1, increment=None, seed=None, debug=False, profile=False, live_output=None, live_output_options={}, timeout=None, resume=None, tau_tol=0.03, **kwargs): """ Function calling simulation of the model. This is typically called by the run function in GillesPy2 model objects and will inherit those parameters which are passed with the model as the arguments this run function. :param model: The model on which the solver will operate. (Deprecated) :type model: gillespy2.Model :param t: Simulation run time. :type t: int or float :param number_of_trajectories: Number of trajectories to simulate. By default number_of_trajectories = 1. :type number_of_trajectories: int :param increment: Save point increment for recording data. :type increment: float :param seed: The random seed for the simulation. Optional, defaults to None. :type seed: int :param debug: Set to True to provide additional debug information about the simulation. :type debug: bool :param profile: Set to True to provide information about step size (tau) taken at each step. :type profile: bool :param live_output: The type of output to be displayed by solver. Can be "progress", "text", or "graph". :type live_output: str :param live_output_options: Contains options for live_output. By default {"interval":1}. "interval" specifies seconds between displaying. "clear_output" specifies if display should be refreshed with each display. :type live_output_options: dict :param timeout: If set, if simulation takes longer than timeout, will exit. :type timeout: int :param resume: Result of a previously run simulation, to be resumed. :type resume: gillespy2.Results :param tau_tol: Tolerance level for Tau leaping algorithm. Larger tolerance values will result in larger tau steps. Default value is 0.03. :type tau_tol: float :returns: A result object containing the results of the simulation. :rtype: gillespy2.Results """ from gillespy2 import log if self is None: # Post deprecation block # raise SimulationError("TauLeapingSolver must be instantiated to run the simulation") # Pre deprecation block log.warning( """ `gillespy2.Model.run(solver=TauLeapingSolver)` is deprecated. You should use `gillespy2.Model.run(solver=TauLeapingSolver(model=gillespy2.Model)) Future releases of GillesPy2 may not support this feature. """ ) self = TauLeapingSolver(model=model, debug=debug, profile=profile) if model is not None: log.warning('model = gillespy2.model is deprecated. Future releases ' 'of GillesPy2 may not support this feature.') if self.model is None: if model is None: raise SimulationError("A model is required to run the simulation.") self.model = copy.deepcopy(model) self.model.compile_prep() self.validate_model(self.model, model) self.validate_sbml_features(model=self.model) self.validate_tspan(increment=increment, t=t) if increment is None: increment = self.model.tspan[-1] - self.model.tspan[-2] if t is None: t = self.model.tspan[-1] self.stop_event = Event() self.pause_event = Event() 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)) # create numpy array for timeline if resume is not None: # start where we last left off if resuming a simulatio lastT = resume['time'][-1] step = lastT - resume['time'][-2] timeline = np.arange(lastT, t+step, step) else: timeline = np.linspace(0, t, int(round(t / increment + 1))) species = list(self.model._listOfSpecies.keys()) trajectory_base, tmpSpecies = nputils.numpy_trajectory_base_initialization(self.model, number_of_trajectories, timeline, species, resume=resume) # total_time and curr_state are list of len 1 so that __run receives reference if resume is not None: total_time = [resume['time'][-1]] else: total_time = [0] curr_state = [None] live_grapher = [None] sim_thread = Thread(target=self.___run, args=(curr_state, total_time, timeline, trajectory_base, tmpSpecies, live_grapher,), kwargs={'t': t, 'number_of_trajectories': number_of_trajectories, 'increment': increment, 'seed': seed, 'debug': debug, 'resume': resume, 'timeout': timeout, 'tau_tol': tau_tol }) try: time = 0 sim_thread.start() if live_output is not None: import gillespy2.core.liveGraphing live_output_options['type'] = live_output gillespy2.core.liveGraphing.valid_graph_params( live_output_options) if resume is not None: resumeTest = True # If resuming, relay this information to live_grapher else: resumeTest = False live_grapher[ 0] = gillespy2.core.liveGraphing.LiveDisplayer(self.model, timeline, number_of_trajectories, live_output_options, resume=resumeTest) display_timer = gillespy2.core.liveGraphing.RepeatTimer( live_output_options['interval'], live_grapher[0].display, args=(curr_state, total_time, trajectory_base, live_output ) ) display_timer.start() if timeout is not None: while sim_thread.is_alive(): sim_thread.join(.1) time += .1 if time >= timeout: break else: while sim_thread.is_alive(): sim_thread.join(.1) if live_grapher[0] is not None: display_timer.cancel() self.stop_event.set() while self.result is None: pass except KeyboardInterrupt: if live_output: display_timer.pause = True display_timer.cancel() self.pause_event.set() while self.result is None: pass if hasattr(self, 'has_raised_exception'): raise SimulationError( f"Error encountered while running simulation:\nReturn code: {int(self.rc)}.\n" ) from self.has_raised_exception return Results.build_from_solver_results(self, live_output_options)
def run(self=None, model=None, t=None, number_of_trajectories=1, increment=None, integrator='lsoda', integrator_options={}, live_output=None, live_output_options={}, timeout=None, resume=None, **kwargs): """ :param model: The model on which the solver will operate. (Deprecated) :type model: gillespy2.Model :param t: End time of simulation. :type t: int or float :param number_of_trajectories: Number of trajectories to simulate. By default number_of_trajectories = 1. This is deterministic and will always have same results. :type number_of_trajectories: int :param increment: Time step increment for plotting. :type increment: float :param integrator: integrator to be used from scipy.integrate.ode. Options include 'vode', 'zvode', 'lsoda', 'dopri5', and 'dop853'. For more details, see https://docs.scipy.org/doc/scipy/reference/generated/scipy.integrate.ode.html :type integrator: str :param integrator_options: a dictionary containing options to the scipy integrator. for a list of options, see https://docs.scipy.org/doc/scipy/reference/generated/scipy.integrate.ode.html. Example use: {max_step : 0, rtol : .01} :type integrator_options: dict :param live_output: The type of output to be displayed by solver. Can be "progress", "text", or "graph". :type live_output: str :param live_output_options: dictionary contains options for live_output. By default {"interval":1}. "interval" specifies seconds between displaying. "clear_output" specifies if display should be refreshed with each display. :type live_output_options: dict :param timeout: If set, if simulation takes longer than timeout, will exit. :type timeout: int :param resume: Result of a previously run simulation, to be resumed. :type resume: gillespy2.Results :returns: A result object containing the results of the simulation. :rtype: gillespy2.Results """ from gillespy2 import log if self is None: # Post deprecation block # raise SimulationError("ODESolver must be instantiated to run the simulation") # Pre deprecation block log.warning( """ `gillespy2.Model.run(solver=ODESolver)` is deprecated. You should use `gillespy2.Model.run(solver=ODESolver(model=gillespy2.Model)) Future releases of GillesPy2 may not support this feature. """ ) self = ODESolver(model=model) if model is not None: log.warning('model = gillespy2.model is deprecated. Future releases ' 'of GillesPy2 may not support this feature.') if self.model is None: if model is None: raise SimulationError("A model is required to run the simulation.") self.model = copy.deepcopy(model) self.model.compile_prep() self.validate_model(self.model, model) self.validate_sbml_features(model=self.model) self.validate_tspan(increment=increment, t=t) if increment is None: increment = self.model.tspan[-1] - self.model.tspan[-2] if t is None: t = self.model.tspan[-1] self.stop_event = Event() self.pause_event = Event() 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)) if number_of_trajectories > 1: log.warning("Generating duplicate trajectories for model with ODE Solver. " "Consider running with only 1 trajectory.") if resume is not None: # start where we last left off if resuming a simulation lastT = resume['time'][-1] step = lastT - resume['time'][-2] timeline = np.arange(lastT, t+step, step) else: timeline = np.linspace(0, t, int(round(t / increment + 1))) species = list(self.model._listOfSpecies.keys()) trajectory_base, tmpSpecies = nputils.numpy_trajectory_base_initialization(self.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]] else: curr_time = [0] # Current Simulation Time curr_state = [None] live_grapher = [None] sim_thread = Thread(target=self.___run, args=(curr_state, curr_time, timeline, trajectory_base, tmpSpecies, live_grapher,), kwargs={'t': t, 'number_of_trajectories': number_of_trajectories, 'increment': increment, 'resume': resume, 'integrator': integrator, 'integrator_options': integrator_options, }) try: time = 0 sim_thread.start() if live_output is not None: import gillespy2.core.liveGraphing live_output_options['type'] = live_output gillespy2.core.liveGraphing.valid_graph_params(live_output_options) if resume is not None: resumeTest = True # If resuming, relay this information to live_grapher else: resumeTest = False live_grapher[0] = gillespy2.core.liveGraphing.LiveDisplayer(self.model, timeline, number_of_trajectories, live_output_options, resume=resumeTest) display_timer = gillespy2.core.liveGraphing.RepeatTimer(live_output_options['interval'], live_grapher[0].display, args=(curr_state, curr_time, trajectory_base,live_output)) display_timer.start() if timeout is not None: while sim_thread.is_alive(): sim_thread.join(.1) time += .1 if time >= timeout: break else: while sim_thread.is_alive(): sim_thread.join(.1) if live_grapher[0] is not None: display_timer.cancel() self.stop_event.set() while self.result is None: pass except KeyboardInterrupt: if live_output: display_timer.pause = True display_timer.cancel() self.pause_event.set() while self.result is None: pass if hasattr(self, 'has_raised_exception'): raise SimulationError( f"Error encountered while running simulation:\nReturn code: {int(self.rc)}.\n" ) from self.has_raised_exception return Results.build_from_solver_results(self, live_output_options)
def run(self, solver=None, **solver_args): """ Function calling simulation of the model. There are a number of parameters to be set here. Return ---------- If show_labels is False, returns a numpy array of arrays of species population data. If show_labels is True and number_of_trajectories is 1, returns a results object that inherits UserDict and supports plotting functions. If show_labels is False and number_of_trajectories is greater than 1, returns an ensemble_results object that inherits UserList and contains results objects and supports ensemble graphing. Attributes ---------- number_of_trajectories : int The number of times to sample the chemical master equation. Each trajectory will be returned at the end of the simulation. Optional, defaults to 1. seed : int The random seed for the simulation. Optional, defaults to None. solver : gillespy.GillesPySolver The solver by which to simulate the model. This solver object may be initialized separately to specify an algorithm. Optional, defaults to ssa solver. show_labels: bool (True) If true, simulation returns a list of trajectories, where each list entry is a dictionary containing key value pairs of species : trajectory. If false, returns a numpy array with shape [traj_no, time, species] switch_tol: float Tolerance for Continuous/Stochastic representation of species, based on coefficient of variance for each step. tau_tol: float Relative error tolerance value for calculating tau step between 0.0 and 1.0 integrator: String integrator to be used form scipy.integrate.ode. Options include 'vode', 'zvode', 'lsoda', 'dopri5', and 'dop835'. For more details, see https://docs.scipy.org/doc/scipy/reference/generated/scipy.integrate.ode.html integrator_options: dictionary contains options to the scipy integrator. for a list of options, see https://docs.scipy.org/doc/scipy/reference/generated/scipy.integrate.ode.html. Example use: {max_step : 0, rtol : .01} """ if solver is not None: if ((isinstance(solver, type) and issubclass(solver, GillesPySolver))) or issubclass( type(solver), GillesPySolver): solver_results = solver.run(model=self, t=self.tspan[-1], increment=self.tspan[-1] - self.tspan[-2], **solver_args) else: raise SimulationError( "argument 'solver' to run() must be a subclass of GillesPySolver" ) else: from gillespy2.solvers.auto import SSASolver solver = SSASolver solver_results = SSASolver.run(model=self, t=self.tspan[-1], increment=self.tspan[-1] - self.tspan[-2], **solver_args) if isinstance(solver_results[0], (np.ndarray)): return solver_results if len(solver_results) is 1: return Results(data=solver_results[0], model=self, solver_name=solver.name) if len(solver_results) > 1: results_list = [] for i in range(0, solver_args.get('number_of_trajectories')): results_list.append( Results(data=solver_results[i], model=self, solver_name=solver.name)) return EnsembleResults(results_list) else: raise ValueError( "number_of_trajectories must be non-negative and non-zero")