Source code for cayenne.results

"""
    Module that defines the `Results` class
"""

from collections.abc import Collection
from typing import List, Tuple, Iterator
from warnings import warn

import numpy as np


[docs]class Results(Collection): """ A class that stores simulation results and provides methods to access them Parameters ---------- species_names : List[str] List of species names rxn_names : List[str] List of reaction names t_list: List[float] List of time points for each repetition x_list: List[np.ndarray] List of system states for each repetition status_list: List[int] List of return status for each repetition algorithm: str Algorithm used to run the simulation sim_seeds: List[int] List of seeds used for the simulation Notes ----- The status indicates the status of the simulation at exit. Each repetition will have a status associated with it, and these are accessible through the ``status_list``. 1: Succesful completion, terminated when ``max_iter`` iterations reached. 2: Succesful completion, terminated when ``max_t`` crossed. 3: Succesful completion, terminated when all species went extinct. -1: Failure, order greater than 3 detected. -2: Failure, propensity zero without extinction. """ def __init__( self, species_names: List[str], rxn_names: List[str], t_list: List[np.ndarray], x_list: List[np.ndarray], status_list: List[int], algorithm: str, sim_seeds: List[int], ) -> None: self.species_names = species_names self.rxn_names = rxn_names self.x_list = x_list self.t_list = t_list self.status_list = status_list self.algorithm = algorithm self.sim_seeds = sim_seeds if not self._check_consistency(): raise ValueError("Inconsistent results passed") def _check_consistency(self) -> bool: """ Check consistency of results Returns ------- bool True if results are consistent False otherwise """ if ( len(self.x_list) == len(self.t_list) == len(self.status_list) == len(self.sim_seeds) ): pass else: return False for x, t, status in self: if x.shape[0] != t.shape[0]: return False if x.shape[1] != len(self.species_names): return False if not isinstance(status, int): return False return True def __repr__(self) -> str: """ Return summary of simulation. Returns ------- summary: str Summary of the simulation with length of simulation, algorithm and seeds used. """ summary = f"<Results species={self.species_names} n_rep={len(self)} " summary = summary + f"algorithm={self.algorithm} sim_seeds={self.sim_seeds}>" return summary def __str__(self) -> str: """ Return self.__repr__() """ return self.__repr__() def __iter__(self) -> Iterator[Tuple[np.ndarray, np.ndarray, int]]: """ Iterate over each repetition """ return zip(self.x_list, self.t_list, self.status_list) def __len__(self) -> int: """ Return number of repetitions in simulation Returns ------- n_rep: int Number of repetitions in simulation """ n_rep = len(self.x_list) return n_rep def __contains__(self, ind: int): """ Returns True if ind is one of the repetition numbers """ if ind < len(self): return True else: return False def __getitem__(self, ind: int) -> Tuple[np.ndarray, np.ndarray, int]: """ Return sim. state, time points and status of repetition no. `ind` Parameters ---------- ind: int Index of the repetition in the simulation Returns ------- x_ind: np.ndarray Simulation status of repetition no. `ind` t_ind: np.ndarray Time points of repetition no. `ind` status_ind Simulation end status of repetition no. `ind` """ if ind in self: x_ind = self.x_list[ind] t_ind = self.t_list[ind] status_ind = self.status_list[ind] else: raise IndexError(f"{ind} out of bounds") return x_ind, t_ind, status_ind @property def final(self) -> Tuple[np.ndarray, np.ndarray]: """ Returns the final times and states of the system in the simulations Returns ------- Tuple[np.ndarray, np.ndarray] The final times and states of the sytem """ final_times = np.array([v[1][-1] for v in self]) final_states = np.array([v[0][-1, :] for v in self]) return final_times, final_states
[docs] def get_state(self, t: float) -> List[np.ndarray]: """ Returns the states of the system at time point t. Parameters ---------- t: float Time point at which states are wanted. Returns ------- List[np.ndarray] The states of the system at `t` for all repetitions. Raises ------ UserWarning If simulation ends before `t` but system does not reach extinction. """ states: List[np.ndarray] = [] e = np.finfo(float).eps * t t = t + e for x_array, t_array, s in self: ind = np.searchsorted(t_array, t) ind = ind - 1 if ind > 0 else ind if ind == len(t_array) - 1: states.append(x_array[-1, :]) if s != 3: warn(f"Simulation ended before {t}, returning last state.") else: x_interp = np.zeros(x_array.shape[1]) if self.algorithm != "direct": for ind2 in range(x_array.shape[1]): x_interp[ind2] = np.interp( t, [t_array[ind], t_array[ind + 1]], [x_array[ind, ind2], x_array[ind + 1, ind2]], ) states.append(x_interp) else: states.append(x_array[ind, :]) return states
[docs] def get_species(self, species_names: List[str]) -> List[np.ndarray]: """ Returns the species concentrations only for the species in species_names Parameters --------- species_names : List[str] The names of the species as a list Returns ------ List[np.ndarray] Simulation output of the selected species. """ x_list_curated = [] species_inds = [self.species_names.index(s) for s in species_names] for rep_ind in range(len(self)): x_list_curated.append(self[rep_ind][0][:, species_inds]) return x_list_curated