def test_simres_dataframe():
    """ Test SimulationResult.dataframe() """

    tspan1 = np.linspace(0, 100, 100)
    tspan2 = np.linspace(50, 100, 50)
    tspan3 = np.linspace(100, 150, 100)
    model = tyson_oscillator.model
    sim = ScipyOdeSimulator(model, integrator='lsoda')
    simres1 = sim.run(tspan=tspan1)
    # Check retrieving a single simulation dataframe
    df_single = simres1.dataframe

    # Generate multiple trajectories
    trajectories1 = simres1.species
    trajectories2 = sim.run(tspan=tspan2).species
    trajectories3 = sim.run(tspan=tspan3).species

    # Try a simulation result with two different tspan lengths
    sim = ScipyOdeSimulator(model, param_values={'k6' : [1.,1.]}, integrator='lsoda')
    simres = SimulationResult(sim, [tspan1, tspan2], [trajectories1, trajectories2])
    df = simres.dataframe

    assert df.shape == (len(tspan1) + len(tspan2),
                        len(model.species) + len(model.observables))

    # Next try a simulation result with two identical tspan lengths, stacked
    # into a single 3D array of trajectories
    simres2 = SimulationResult(sim, [tspan1, tspan3],
                               np.stack([trajectories1, trajectories3]))
    df2 = simres2.dataframe

    assert df2.shape == (len(tspan1) + len(tspan3),
                         len(model.species) + len(model.observables))
Beispiel #2
0
    def run(self, tspan=None, initials=None, param_values=None):
        """
        Run a simulation and returns the result (trajectories)

        .. note::
            In early versions of the Simulator class, ``tspan``, ``initials``
            and ``param_values`` supplied to this method persisted to future
            :func:`run` calls. This is no longer the case.

        Parameters
        ----------
        tspan
        initials
        param_values
            See parameter definitions in :class:`ScipyOdeSimulator`.

        Returns
        -------
        A :class:`SimulationResult` object
        """
        super(ScipyOdeSimulator, self).run(tspan=tspan,
                                           initials=initials,
                                           param_values=param_values,
                                           _run_kwargs=[])
        n_sims = len(self.param_values)
        trajectories = np.ndarray(
            (n_sims, len(self.tspan), len(self._model.species)))
        for n in range(n_sims):
            self._logger.info('Running simulation %d of %d', n + 1, n_sims)
            if self.integrator == 'lsoda':
                trajectories[n] = scipy.integrate.odeint(
                    self.func,
                    self.initials[n],
                    self.tspan,
                    Dfun=self.jac_fn,
                    args=(self.param_values[n], ),
                    **self.opts)
            else:
                self.integrator.set_initial_value(self.initials[n],
                                                  self.tspan[0])
                # Set parameter vectors for RHS func and Jacobian
                self.integrator.set_f_params(self.param_values[n])
                if self._use_analytic_jacobian:
                    self.integrator.set_jac_params(self.param_values[n])
                trajectories[n][0] = self.initials[n]
                i = 1
                while self.integrator.successful() and self.integrator.t < \
                        self.tspan[-1]:
                    self._logger.log(EXTENDED_DEBUG,
                                     'Simulation %d/%d Integrating t=%g',
                                     n + 1, n_sims, self.integrator.t)
                    trajectories[n][i] = self.integrator.integrate(
                        self.tspan[i])
                    i += 1
                if self.integrator.t < self.tspan[-1]:
                    trajectories[n, i:, :] = 'nan'

        tout = np.array([self.tspan] * n_sims)
        self._logger.info('All simulation(s) complete')
        return SimulationResult(self, tout, trajectories)
Beispiel #3
0
def test_save_load_observables_expressions():
    buff = io.BytesIO()
    tspan = np.linspace(0, 100, 100)
    sim = ScipyOdeSimulator(tyson_oscillator.model, tspan).run()
    sim.save(buff, include_obs_exprs=True)

    sim2 = SimulationResult.load(buff)
    assert len(sim2.observables) == len(tspan)
    # Tyson oscillator doesn't have expressions
    assert_raises(ValueError, lambda: sim2.expressions)
Beispiel #4
0
    def run(self,
            tspan=None,
            initials=None,
            param_values=None,
            n_runs=1,
            method='ssa',
            output_dir=None,
            output_file_basename=None,
            cleanup=None,
            population_maps=None,
            **additional_args):
        """
        Simulate a model using BioNetGen

        Parameters
        ----------
        tspan: vector-like
            time span of simulation
        initials: vector-like, optional
            initial conditions of model
        param_values : vector-like or dictionary, optional
            Values to use for every parameter in the model. Ordering is
            determined by the order of model.parameters.
            If not specified, parameter values will be taken directly from
            model.parameters.
        n_runs: int
            number of simulations to run
        method : str
            Type of simulation to run. Must be one of:

                * 'ssa' - Stochastic Simulation Algorithm (direct method with
                  propensity sorting)

                * 'nf' - Stochastic network free simulation with NFsim.
                  Performs Hybrid Particle/Population simulation if
                  population_maps argument is supplied

                * 'pla' - Partioned-leaping algorithm (variant of tau-leaping
                  algorithm)

                * 'ode' - ODE simulation (Sundials CVODE algorithm)

        output_dir : string, optional
            Location for temporary files generated by BNG. If None (the
            default), uses a temporary directory provided by the system. A
            temporary directory with a random name is created within the
            supplied location.
        output_file_basename : string, optional
            This argument is used as a prefix for the temporary BNG
            output directory, rather than the individual files.
        cleanup : bool, optional
            If True (default), delete the temporary files after the
            simulation is finished. If False, leave them in place (Useful for
            debugging). The default value, None, means to use the value
            specified in :py:func:`__init__`.
        population_maps: list of PopulationMap
            List of :py:class:`PopulationMap` objects for hybrid
            particle/population modeling. Only used when method='nf'.
        additional_args: kwargs, optional
            Additional arguments to pass to BioNetGen

        Examples
        --------

        Simulate a model using network free simulation (NFsim):

        >>> from pysb.examples import robertson
        >>> from pysb.simulator.bng import BngSimulator
        >>> model = robertson.model
        >>> sim = BngSimulator(model, tspan=np.linspace(0, 1))
        >>> x = sim.run(n_runs=1, method='nf')


        """
        super(BngSimulator, self).run(tspan=tspan,
                                      initials=initials,
                                      param_values=param_values,
                                      _run_kwargs=locals())

        if cleanup is None:
            cleanup = self.cleanup

        if method not in self._SIMULATOR_TYPES:
            raise ValueError("Method must be one of " +
                             str(self._SIMULATOR_TYPES))

        if method != 'nf' and population_maps:
            raise ValueError('population_maps argument is only used when '
                             'method is "nf"')

        if method == 'nf':
            if population_maps is not None and (
                    not isinstance(population_maps, collections.Iterable)
                    or any(not isinstance(pm, PopulationMap)
                           for pm in population_maps)):
                raise ValueError('population_maps should be a list of '
                                 'PopulationMap objects')
            model_additional_species = self.initials_dict.keys()
        else:
            model_additional_species = None

        tspan_lin_spaced = np.allclose(
            self.tspan,
            np.linspace(self.tspan[0], self.tspan[-1], len(self.tspan)))
        if method == 'nf' and (not tspan_lin_spaced or self.tspan[0] != 0.0):
            raise SimulatorException('NFsim requires tspan to be linearly '
                                     'spaced starting at t=0')

        # BNG requires t_start even when supplying sample_times
        additional_args['t_start'] = self.tspan[0]
        if tspan_lin_spaced:
            # Just supply t_end and n_steps
            additional_args['n_steps'] = len(self.tspan) - 1
            additional_args['t_end'] = self.tspan[-1]
        else:
            additional_args['sample_times'] = self.tspan

        additional_args['method'] = method
        additional_args['print_functions'] = True
        verbose_bool = self._logger.logger.getEffectiveLevel() <= logging.DEBUG
        extended_debug = self._logger.logger.getEffectiveLevel() <= \
                         EXTENDED_DEBUG
        additional_args['verbose'] = extended_debug
        params_names = [g.name for g in self._model.parameters]

        n_param_sets = self.initials_length
        total_sims = n_runs * n_param_sets

        self._logger.info('Running %d BNG %s simulations' %
                          (total_sims, method))

        model_to_load = None
        hpp_bngl = None

        if population_maps:
            self._logger.debug('Generating hybrid particle-population model')
            hpp_bngl = generate_hybrid_model(self._model,
                                             population_maps,
                                             model_additional_species,
                                             verbose=extended_debug)
        else:
            model_to_load = self._model

        with BngFileInterface(
                model_to_load,
                verbose=verbose_bool,
                output_dir=output_dir,
                output_prefix=output_file_basename,
                cleanup=cleanup,
                model_additional_species=model_additional_species) as bngfile:
            if hpp_bngl:
                hpp_bngl_filename = os.path.join(bngfile.base_directory,
                                                 'hpp_model.bngl')
                self._logger.debug('HPP BNGL:\n\n' + hpp_bngl)
                with open(hpp_bngl_filename, 'w') as f:
                    f.write(hpp_bngl)
            if method != 'nf':
                # TODO: Write existing netfile if already generated
                bngfile.action('generate_network',
                               overwrite=True,
                               verbose=extended_debug)
            if output_file_basename is None:
                prefix = 'pysb'
            else:
                prefix = output_file_basename

            sim_prefix = 0
            for pset_idx in range(n_param_sets):
                for n in range(len(self.param_values[pset_idx])):
                    bngfile.set_parameter(params_names[n],
                                          self.param_values[pset_idx][n])
                for cp, values in self.initials_dict.items():
                    if population_maps:
                        for pm in population_maps:
                            if pm.complex_pattern.is_equivalent_to(cp):
                                cp = pm.counter_species
                                break
                    bngfile.set_concentration(cp, values[pset_idx])
                for sim_rpt in range(n_runs):
                    tmp = additional_args.copy()
                    tmp['prefix'] = '{}{}'.format(prefix, sim_prefix)
                    bngfile.action('simulate', **tmp)
                    bngfile.action('resetConcentrations')
                    sim_prefix += 1
            if hpp_bngl:
                bngfile.execute(reload_netfile=hpp_bngl_filename,
                                skip_file_actions=True)
            else:
                bngfile.execute()
            if method != 'nf':
                load_equations(self.model, bngfile.net_filename)
            list_of_yfull = \
                BngFileInterface.read_simulation_results_multi(
                [bngfile.base_filename + str(n) for n in range(total_sims)])

        tout = []
        species_out = []
        obs_exp_out = []
        for i in range(total_sims):
            yfull = list_of_yfull[i]
            yfull_view = yfull.view(float).reshape(len(yfull), -1)

            tout.append(yfull_view[:, 0])

            if method == 'nf':
                obs_exp_out.append(yfull_view[:, 1:])
            else:
                species_out.append(yfull_view[:,
                                              1:(len(self.model.species) + 1)])
                if len(self.model.observables) or len(self.model.expressions):
                    obs_exp_out.append(
                        yfull_view[:, (len(self.model.species) +
                                       1):(len(self.model.species) + 1) +
                                   len(self.model.observables) +
                                   len(self.model.expressions_dynamic())])

        return SimulationResult(self,
                                tout=tout,
                                trajectories=species_out,
                                observables_and_expressions=obs_exp_out,
                                simulations_per_param_set=n_runs)
def test_save_load():
    tspan = np.linspace(0, 100, 101)
    model = tyson_oscillator.model
    test_unicode_name = u'Hello \u2603 and \U0001f4a9!'
    model.name = test_unicode_name
    sim = ScipyOdeSimulator(model, integrator='lsoda')
    simres = sim.run(tspan=tspan, param_values={'k6': 1.0})

    sim_rob = ScipyOdeSimulator(robertson.model, integrator='lsoda')
    simres_rob = sim_rob.run(tspan=tspan)

    # Reset equations from any previous network generation
    robertson.model.reset_equations()
    A = robertson.model.monomers['A']

    # NFsim without expressions
    nfsim1 = BngSimulator(robertson.model)
    nfres1 = nfsim1.run(n_runs=2, method='nf', tspan=np.linspace(0, 1))
    # Test attribute saving (text, float, list)
    nfres1.custom_attrs['note'] = 'NFsim without expressions'
    nfres1.custom_attrs['pi'] = 3.14
    nfres1.custom_attrs['some_list'] = [1, 2, 3]

    # NFsim with expressions
    nfsim2 = BngSimulator(expression_observables.model)
    nfres2 = nfsim2.run(n_runs=1, method='nf', tspan=np.linspace(0, 100, 11))

    with tempfile.NamedTemporaryFile() as tf:
        # Cannot have two file handles on Windows
        tf.close()

        simres.save(tf.name, dataset_name='test', append=True)

        # Try to reload when file contains only one dataset and group
        SimulationResult.load(tf.name)

        simres.save(tf.name, append=True)

        # Trying to overwrite an existing dataset gives a ValueError
        assert_raises(ValueError, simres.save, tf.name, append=True)

        # Trying to write to an existing file without append gives an IOError
        assert_raises(IOError, simres.save, tf.name)

        # Trying to write a SimulationResult to the same group with a
        # different model name results in a ValueError
        assert_raises(ValueError, simres_rob.save, tf.name,
                      dataset_name='robertson', group_name=model.name,
                      append=True)

        simres_rob.save(tf.name, append=True)

        # Trying to load from a file with more than one group without
        # specifying group_name should raise a ValueError
        assert_raises(ValueError, SimulationResult.load, tf.name)

        # Trying to load from a group with more than one dataset without
        # specifying a dataset_name should raise a ValueError
        assert_raises(ValueError, SimulationResult.load, tf.name,
                      group_name=model.name)

        # Load should succeed when specifying group_name and dataset_name
        simres_load = SimulationResult.load(tf.name, group_name=model.name,
                                            dataset_name='test')
        assert simres_load._model.name == test_unicode_name

        # Saving network free results requires include_obs_exprs=True,
        # otherwise a warning should be raised
        with warnings.catch_warnings(record=True) as w:
            warnings.simplefilter("always")
            nfres1.save(tf.name, dataset_name='nfsim_no_obs', append=True)
            assert len(w) == 1
            assert issubclass(w[-1].category, UserWarning)

        nfres1.save(tf.name, include_obs_exprs=True,
                    dataset_name='nfsim test', append=True)

        # NFsim load
        nfres1_load = SimulationResult.load(tf.name,
                                            group_name=nfres1._model.name,
                                            dataset_name='nfsim test')

        # NFsim with expression
        nfres2.save(tf.name, include_obs_exprs=True, append=True)
        nfres2_load = SimulationResult.load(tf.name,
                                            group_name=nfres2._model.name)

    _check_resultsets_equal(simres, simres_load)
    _check_resultsets_equal(nfres1, nfres1_load)
    _check_resultsets_equal(nfres2, nfres2_load)
Beispiel #6
0
    def run(self, tspan=None, initials=None, param_values=None):
        """Perform a set of integrations.

        Returns a :class:`.SimulationResult` object.

        Parameters
        ----------
        tspan : list-like, optional
            Time values at which the integrations are sampled. The first and
            last values define the time range.
        initials : list-like, optional
            Initial species concentrations for all simulations. Dimensions are
            number of simulation x number of species.    
        param_values : list-like, optional
            Parameters for all simulations. Dimensions are number of
            simulations x number of parameters.

        Returns
        -------
        A :class:`SimulationResult` object

        Notes
        -----
        1. An exception is thrown if `tspan` is not defined in either
           `__init__`or `run`.
           
        2. If neither `initials` nor `param_values` are defined in either 
           `__init__` or `run` a single simulation is run with the initial 
           concentrations and parameter values defined in the model.

        """
        super(CupSodaSimulator, self).run(tspan=tspan,
                                          initials=initials,
                                          param_values=param_values,
                                          _run_kwargs=[])

        # Create directories for cupSODA input and output files
        _outdirs = {}
        _indirs = {}

        start_time = time.time()

        cmtx = self._get_cmatrix()

        outdir = tempfile.mkdtemp(prefix=self._prefix + '_',
                                  dir=self._base_dir)
        self._logger.debug("Output directory is %s" % outdir)

        # Set up chunking (enforce max # sims per GPU per run)
        n_sims = len(self.param_values)

        chunksize_gpu = self.opts.get('chunksize', None)
        if chunksize_gpu is None:
            chunksize_gpu = n_sims

        chunksize_total = chunksize_gpu * len(self.gpu)

        tout = None
        trajectories = None

        chunks = np.array_split(range(n_sims),
                                np.ceil(n_sims / chunksize_total))

        try:
            for chunk_idx, chunk in enumerate(chunks):
                self._logger.debug('cupSODA chunk {} of {}'.format(
                    (chunk_idx + 1), len(chunks)))

                # Split chunk equally between GPUs
                sims = dict(zip(self.gpu, np.array_split(chunk,
                                                         len(self.gpu))))

                tout, trajectories = self._run_chunk(self.gpu, outdir,
                                                     chunk_idx, cmtx, sims,
                                                     trajectories, tout)
        finally:
            if self._cleanup:
                shutil.rmtree(outdir)

        end_time = time.time()
        self._logger.info("cupSODA + I/O time: {} seconds".format(end_time -
                                                                  start_time))
        return SimulationResult(self, tout, trajectories)
Beispiel #7
0
    def run(self,
            tspan=None,
            initials=None,
            param_values=None,
            n_runs=1,
            algorithm='ssa',
            output_dir=None,
            num_processors=1,
            seed=None,
            method=None,
            stats=False,
            epsilon=None,
            threshold=None):
        """
        Run a simulation and returns the result (trajectories)

        .. note::
            ``tspan``, ``initials`` and ``param_values`` values supplied to
            this method will persist to future :func:`run` calls.

        Parameters
        ----------
        tspan
        initials
        param_values
            See parameter definitions in :class:`StochKitSimulator`.
        n_runs : int
            The number of simulation runs per parameter set. The total
            number of simulations is therefore n_runs * max(len(initials),
            len(param_values))
        algorithm : str
            Choice of 'ssa' (Gillespie's stochastic simulation algorithm) or
            'tau_leaping' (Tau leaping algorithm)
        output_dir : str or None
            Directory for StochKit output, or None for a system-specific
            temporary directory
        num_processors : int
            Number of CPU cores for StochKit to use (default: 1)
        seed : int or None
            A random number seed for StochKit. Set to any integer value for
            deterministic behavior.
        method : str or None
            StochKit "method" argument, default None. Only used by StochKit
            2.1 (not yet released at time of writing).
        stats : bool
            Ask StochKit to generate simulation summary statistics if True
        epsilon : float or None
            Tolerance parameter for tau-leaping algorithm
        threshold : int or None
            Threshold parameter for tau-leaping algorithm

        Returns
        -------
        A :class:`SimulationResult` object
        """
        super(StochKitSimulator, self).run(tspan=tspan,
                                           initials=initials,
                                           param_values=param_values)

        self._logger.info('Running StochKit with {:d} parameter sets, '
                          '{:d} repeats ({:d} simulations total)'.format(
                              len(self.initials), n_runs,
                              len(self.initials) * n_runs))

        if output_dir is None:
            self._outdir = tempfile.mkdtemp()
        else:
            self._outdir = output_dir

        # Calculate time intervals and validate
        t_range = self.tspan[-1] - self.tspan[0]
        t_length = len(self.tspan)
        if not np.allclose(self.tspan, np.linspace(0, self.tspan[-1],
                                                   t_length)):
            raise SimulatorException('StochKit requires tspan to be linearly '
                                     'spaced starting at t=0')

        try:
            trajectories = self._run_stochkit(t=t_range,
                                              number_of_trajectories=n_runs,
                                              t_length=t_length,
                                              seed=seed,
                                              algorithm=algorithm,
                                              method=method,
                                              num_processors=num_processors,
                                              stats=stats,
                                              epsilon=epsilon,
                                              threshold=threshold)
        finally:
            if self.cleanup:
                try:
                    shutil.rmtree(self._outdir)
                except OSError:
                    pass

        # set output time points
        trajectories_array = np.array(trajectories)
        self.tout = trajectories_array[:, :, 0] + self.tspan[0]
        # species
        species = trajectories_array[:, :, 1:]
        return SimulationResult(self,
                                self.tout,
                                species,
                                simulations_per_param_set=n_runs)
Beispiel #8
0
def test_save_load():
    tspan = np.linspace(0, 100, 101)
    # Make a copy of model so other tests etc. don't see the changed name.
    model = copy.deepcopy(tyson_oscillator.model)
    test_unicode_name = u'Hello \u2603 and \U0001f4a9!'
    model.name = test_unicode_name
    sim = ScipyOdeSimulator(model, integrator='lsoda')
    simres = sim.run(tspan=tspan, param_values={'k6': 1.0})

    sim_rob = ScipyOdeSimulator(robertson.model, integrator='lsoda')
    simres_rob = sim_rob.run(tspan=tspan)

    # Reset equations from any previous network generation
    robertson.model.reset_equations()
    A = robertson.model.monomers['A']

    # NFsim without expressions
    nfsim1 = BngSimulator(robertson.model)
    nfres1 = nfsim1.run(n_runs=2, method='nf', tspan=np.linspace(0, 1))
    # Test attribute saving (text, float, list)
    nfres1.custom_attrs['note'] = 'NFsim without expressions'
    nfres1.custom_attrs['pi'] = 3.14
    nfres1.custom_attrs['some_list'] = [1, 2, 3]

    # NFsim with expressions
    nfsim2 = BngSimulator(expression_observables.model)
    nfres2 = nfsim2.run(n_runs=1, method='nf', tspan=np.linspace(0, 100, 11))

    with tempfile.NamedTemporaryFile() as tf:
        # Cannot have two file handles on Windows
        tf.close()

        simres.save(tf.name, dataset_name='test', append=True)

        # Try to reload when file contains only one dataset and group
        SimulationResult.load(tf.name)

        simres.save(tf.name, append=True)

        # Trying to overwrite an existing dataset gives a ValueError
        assert_raises(ValueError, simres.save, tf.name, append=True)

        # Trying to write to an existing file without append gives an IOError
        assert_raises(IOError, simres.save, tf.name)

        # Trying to write a SimulationResult to the same group with a
        # different model name results in a ValueError
        assert_raises(ValueError, simres_rob.save, tf.name,
                      dataset_name='robertson', group_name=model.name,
                      append=True)

        simres_rob.save(tf.name, append=True)

        # Trying to load from a file with more than one group without
        # specifying group_name should raise a ValueError
        assert_raises(ValueError, SimulationResult.load, tf.name)

        # Trying to load from a group with more than one dataset without
        # specifying a dataset_name should raise a ValueError
        assert_raises(ValueError, SimulationResult.load, tf.name,
                      group_name=model.name)

        # Load should succeed when specifying group_name and dataset_name
        simres_load = SimulationResult.load(tf.name, group_name=model.name,
                                            dataset_name='test')
        assert simres_load._model.name == test_unicode_name

        # Saving network free results requires include_obs_exprs=True,
        # otherwise a warning should be raised
        with warnings.catch_warnings(record=True) as w:
            warnings.simplefilter("always")
            nfres1.save(tf.name, dataset_name='nfsim_no_obs', append=True)
            assert len(w) == 1
            assert issubclass(w[-1].category, UserWarning)

        nfres1.save(tf.name, include_obs_exprs=True,
                    dataset_name='nfsim test', append=True)

        # NFsim load
        nfres1_load = SimulationResult.load(tf.name,
                                            group_name=nfres1._model.name,
                                            dataset_name='nfsim test')

        # NFsim with expression
        nfres2.save(tf.name, include_obs_exprs=True, append=True)
        nfres2_load = SimulationResult.load(tf.name,
                                            group_name=nfres2._model.name)

    _check_resultsets_equal(simres, simres_load)
    _check_resultsets_equal(nfres1, nfres1_load)
    _check_resultsets_equal(nfres2, nfres2_load)
Beispiel #9
0
    def run(self, tspan=None, initials=None, param_values=None):
        """Perform a set of integrations.

        Returns a :class:`.SimulationResult` object.

        Parameters
        ----------
        tspan : list-like, optional
            Time values at which the integrations are sampled. The first and
            last values define the time range.
        initials : list-like, optional
            Initial species concentrations for all simulations. Dimensions are
            number of simulation x number of species.    
        param_values : list-like, optional
            Parameters for all simulations. Dimensions are number of
            simulations x number of parameters.

        Returns
        -------
        A :class:`SimulationResult` object

        Notes
        -----
        1. An exception is thrown if `tspan` is not defined in either
           `__init__`or `run`.
           
        2. If neither `initials` nor `param_values` are defined in either 
           `__init__` or `run` a single simulation is run with the initial 
           concentrations and parameter values defined in the model.

        """
        super(CupSodaSimulator, self).run(tspan=tspan,
                                          initials=initials,
                                          param_values=param_values,
                                          _run_kwargs=[])

        # Create directories for cupSODA input and output files
        self.outdir = tempfile.mkdtemp(prefix=self._prefix + '_',
                                       dir=self._base_dir)

        self._logger.debug("Output directory is %s" % self.outdir)
        _cupsoda_infiles_dir = os.path.join(self.outdir, "INPUT")
        os.mkdir(_cupsoda_infiles_dir)
        self._cupsoda_outfiles_dir = os.path.join(self.outdir, "OUTPUT")

        # Path to cupSODA executable
        bin_path = get_path('cupsoda')

        # Create cupSODA input files
        self._create_input_files(_cupsoda_infiles_dir)

        # Build command
        # ./cupSODA input_model_folder blocks output_folder simulation_
        # file_prefix gpu_number fitness_calculation memory_use dump
        command = [
            bin_path, _cupsoda_infiles_dir,
            str(self.n_blocks), self._cupsoda_outfiles_dir, self._prefix,
            str(self.gpu), '0', self._memory_usage,
            str(self._cupsoda_verbose)
        ]

        self._logger.info("Running cupSODA: " + ' '.join(command))
        start_time = time.time()
        # Run simulation and return trajectories
        p = subprocess.Popen(command,
                             stdout=subprocess.PIPE,
                             stderr=subprocess.PIPE)
        # for line in iter(p.stdout.readline, b''):
        #     if 'Running time' in line:
        #         self._logger.info(line[:-1])
        (p_out, p_err) = p.communicate()
        p_out = p_out.decode('utf-8')
        p_err = p_err.decode('utf-8')
        logger_level = self._logger.logger.getEffectiveLevel()
        if logger_level <= logging.INFO:
            run_time_match = self._running_time_regex.search(p_out)
            if run_time_match:
                self._logger.info('cupSODA reported time: {} '
                                  'seconds'.format(run_time_match.group(1)))
        self._logger.debug('cupSODA stdout:\n' + p_out)
        if p_err:
            self._logger.error('cupsoda strerr:\n' + p_err)
        if p.returncode:
            raise SimulatorException(
                p_out.rstrip("at line") + "\n" + p_err.rstrip())
        tout, trajectories = self._load_trajectories(
            self._cupsoda_outfiles_dir)
        if self._cleanup:
            shutil.rmtree(self.outdir)
        end_time = time.time()
        self._logger.info("cupSODA + I/O time: {} seconds".format(end_time -
                                                                  start_time))
        return SimulationResult(self, tout, trajectories)
Beispiel #10
0
    def run(self,
            tspan=None,
            initials=None,
            param_values=None,
            num_processors=1):
        """
        Run a simulation and returns the result (trajectories)

        .. note::
            In early versions of the Simulator class, ``tspan``, ``initials``
            and ``param_values`` supplied to this method persisted to future
            :func:`run` calls. This is no longer the case.

        Parameters
        ----------
        tspan
        initials
        param_values
            See parameter definitions in :class:`ScipyOdeSimulator`.
        num_processors : int
            Number of processes to use (default: 1). Set to a larger number
            (e.g. the number of CPU cores available) for parallel execution of
            simulations. This is only useful when simulating with more than one
            set of initial conditions and/or parameters.

        Returns
        -------
        A :class:`SimulationResult` object
        """
        super(ScipyOdeSimulator, self).run(tspan=tspan,
                                           initials=initials,
                                           param_values=param_values,
                                           _run_kwargs=[])
        n_sims = len(self.param_values)

        num_species = len(self._model.species)
        num_odes = len(self._model.odes)
        results = []
        if num_processors == 1:
            self._logger.debug('Single processor (serial) mode')
        else:
            self._logger.debug('Multi-processor (parallel) mode using {} '
                               'processes'.format(num_processors))
        with SerialExecutor() if num_processors == 1 else \
                ProcessPoolExecutor(max_workers=num_processors) as executor:
            for n in range(n_sims):
                results.append(
                    executor.submit(
                        _integrator_process,
                        self._code_eqs,
                        self._jac_eqs,
                        num_species,
                        num_odes,
                        self.initials[n],
                        self.tspan,
                        self.param_values[n],
                        self._init_kwargs.get('integrator', 'vode'),
                        compiler=self._compiler,
                        integrator_opts=self.opts,
                        compiler_directives=self._compiler_directives))
            trajectories = [r.result() for r in results]

        tout = np.array([self.tspan] * n_sims)
        self._logger.info('All simulation(s) complete')
        return SimulationResult(self, tout, trajectories)
Beispiel #11
0
    def run(self,
            tspan=None,
            initials=None,
            param_values=None,
            num_processors=1):
        """
        Run a simulation and returns the result (trajectories)

        .. note::
            In early versions of the Simulator class, ``tspan``, ``initials``
            and ``param_values`` supplied to this method persisted to future
            :func:`run` calls. This is no longer the case.

        Parameters
        ----------
        tspan
        initials
        param_values
            See parameter definitions in :class:`ScipyOdeSimulator`.
        num_processors : int
            Number of processes to use (default: 1). Set to a larger number
            (e.g. the number of CPU cores available) for parallel execution of
            simulations. This is only useful when simulating with more than one
            set of initial conditions and/or parameters.

        Returns
        -------
        A :class:`SimulationResult` object
        """
        super(AmiciSimulator, self).run(tspan=tspan,
                                        initials=initials,
                                        param_values=param_values,
                                        _run_kwargs=[])
        n_sims = len(self.param_values)

        num_processors = min(n_sims, num_processors)

        if num_processors == 1:
            self._logger.debug('Single processor (serial) mode')
        else:
            if not amici.compiledWithOpenMP():
                raise EnvironmentError(
                    'AMICI/model was not compiled with openMP support, which '
                    'is required for parallel simulation. Please see '
                    'https://github.com/ICB-DCM/AMICI/blob/master'
                    '/documentation/PYTHON.md for details on how to compile '
                    'AMICI with openMP support.')
            self._logger.debug('Multi-processor (parallel) mode using {} '
                               'processes'.format(num_processors))

        edatas = self._simulationspecs_to_edatas()

        rdatas = amici.runAmiciSimulations(model=self.amici_model,
                                           solver=self.amici_solver,
                                           edata_list=edatas,
                                           failfast=False,
                                           num_threads=num_processors)

        self._logger.info('All simulation(s) complete')
        return SimulationResult(self, np.array([self.tspan] * n_sims),
                                self._rdatas_to_trajectories(rdatas))
Beispiel #12
0
    def run(self,
            tspan=None,
            initials=None,
            param_values=None,
            n_runs=1,
            output_dir=None,
            output_file_basename=None,
            cleanup=None,
            **additional_args):
        """
        Simulate a model using Kappa

        Parameters
        ----------
        tspan: vector-like
            time span of simulation
        initials: vector-like, optional
            initial conditions of model
        param_values : vector-like or dictionary, optional
            Values to use for every parameter in the model. Ordering is
            determined by the order of model.parameters.
            If not specified, parameter values will be taken directly from
            model.parameters.
        n_runs: int
            number of simulations to run
        output_dir : string, optional
            Location for temporary files generated by Kappa. If None (the
            default), uses a temporary directory provided by the system. A
            temporary directory with a random name is created within the
            supplied location.
        output_file_basename : string, optional
            This argument is used as a prefix for the temporary Kappa
            output directory, rather than the individual files.
        cleanup : bool, optional
            If True (default), delete the temporary files after the
            simulation is finished. If False, leave them in place (Useful for
            debugging). The default value, None, means to use the value
            specified in :py:func:`__init__`.
        additional_args: kwargs, optional
            Additional arguments to pass to Kappa

                * seed : int, optional
                    Random number seed for Kappa simulation

                * perturbation : string, optional
                    Optional perturbation language syntax to be appended to the
                    Kappa file. See KaSim manual for more details.

        Examples
        --------

        >>> import numpy as np
        >>> from pysb.examples import michment
        >>> from pysb.simulator import KappaSimulator
        >>> sim = KappaSimulator(michment.model, tspan=np.linspace(0, 1))
        >>> x = sim.run(n_runs=1)


        """
        super(KappaSimulator, self).run(tspan=tspan,
                                        initials=initials,
                                        param_values=param_values,
                                        _run_kwargs=locals())

        if cleanup is None:
            cleanup = self.cleanup

        tspan_lin_spaced = np.allclose(
            self.tspan,
            np.linspace(self.tspan[0], self.tspan[-1], len(self.tspan)))
        if not tspan_lin_spaced or self.tspan[0] != 0.0:
            raise SimulatorException('Kappa requires tspan to be linearly '
                                     'spaced starting at t=0')
        points = len(self.tspan)
        time = self.tspan[-1]
        plot_period = time / (len(self.tspan) - 1)

        if output_file_basename is None:
            output_file_basename = 'tmpKappa_%s_' % self.model.name

        base_directory = tempfile.mkdtemp(prefix=output_file_basename,
                                          dir=output_dir)

        base_filename = os.path.join(base_directory, self.model.name)
        kappa_filename_pattern = base_filename + '_{}.ka'
        out_filename_pattern = base_filename + '_{}_run{}.out'

        base_args = ['-u', 'time', '-l', str(time), '-p', '%.5f' % plot_period]
        if 'seed' in additional_args:
            seed = additional_args.pop('seed')
            base_args.extend(['-seed', str(seed)])

        kasim_path = pf.get_path('kasim')
        n_param_sets = self.initials_length

        gen = KappaGenerator(self.model, _exclude_ic_param=True)
        file_data_base = gen.get_content()

        # Check if a perturbation has been set
        try:
            perturbation = additional_args.pop('perturbation')
        except KeyError:
            perturbation = None

        # Check no unknown arguments have been set
        if additional_args:
            raise ValueError('Unknown argument(s): {}'.format(', '.join(
                additional_args.keys())))

        # Kappa column names, for sanity check
        kappa_col_names = tuple(['time'] +
                                [o.name for o in self.model.observables])
        tout = []
        observable_traj = []
        try:
            for pset_idx in range(n_param_sets):
                file_data = file_data_base + ''
                for param, param_value in zip(self.model.parameters,
                                              self.param_values[pset_idx]):
                    file_data += "%var: '{}' {:e}\n".format(
                        param.name, param_value)
                file_data += '\n'
                for cp, values in self.initials_dict.items():
                    file_data += "%init: {} {}\n".format(
                        values[pset_idx], gen.format_complexpattern(cp))

                # If any perturbation language code has been passed in, add it
                # to the Kappa file:
                if perturbation:
                    file_data += '%s\n' % perturbation

                # Generate the Kappa model code from the PySB model and write
                # it to the Kappa file:
                kappa_filename = kappa_filename_pattern.format(pset_idx)
                with open(kappa_filename, 'w') as kappa_file:
                    self._logger.debug('Kappa file contents:\n\n' + file_data)
                    kappa_file.write(file_data)

                for sim_rpt in range(n_runs):
                    # Run Kappa
                    out_filename = out_filename_pattern.format(
                        pset_idx, sim_rpt)
                    args = [kasim_path] + base_args + [
                        '-i', kappa_filename, '-o', out_filename
                    ]

                    # Run KaSim
                    self._logger.debug('Running: {}'.format(' '.join(args)))
                    p = subprocess.Popen(args,
                                         stdout=subprocess.PIPE,
                                         stderr=subprocess.PIPE,
                                         cwd=base_directory)

                    for line in p.stdout:
                        self._logger.debug('@@' + line.decode('utf8')[:-1])
                    (p_out, p_err) = p.communicate()

                    if p.returncode:
                        raise KasimInterfaceError(
                            p_out.decode('utf8') + '\n' + p_err.decode('utf8'))

                    # The simulation data, as a numpy array
                    data = _parse_kasim_outfile(out_filename)
                    # Sanity check that observables are in correct order
                    assert data.dtype.names == kappa_col_names
                    data = data.view('<f8')
                    # Handle case with single row output
                    if data.ndim == 1:
                        data.shape = (1, data.shape[0])
                    # Parse into format
                    tout.append(data[:, 0])
                    observable_traj.append(data[:, 1:])
        finally:
            if cleanup:
                shutil.rmtree(base_directory)

        return SimulationResult(self,
                                tout=tout,
                                observables_and_expressions=observable_traj,
                                simulations_per_param_set=n_runs)