Skip to content

Simulation API

Simulation

A Facade for running a simulation from a defined Model.

Source code in commol/api/simulation.py
class Simulation:
    """
    A Facade for running a simulation from a defined Model.
    """

    def __init__(self, model: Model):
        """
        Initializes the simulation engine from a Pydantic Model definition.

        Parameters
        ----------
        model : Model
            A fully constructed and validated model object.
        """
        logging.info(f"Initializing Simulation with model: '{model.name}'")
        self.model_definition: Model = model
        self._engine: "DifferenceEquationsProtocol" = self._initialize_engine()

        self._compartments: list[str] = self._engine.compartments
        logging.info(
            f"Simulation engine ready. Total compartments: {len(self._compartments)}"
        )

    def _initialize_engine(self) -> "DifferenceEquationsProtocol":
        """Internal method to set up the Rust backend."""
        logging.info("Preparing model definition for Rust serialization...")
        model_json = self.model_definition.model_dump_json()

        rust_model_instance: "RustModelProtocol" = core.Model.from_json(model_json)
        logging.info("Rust model instance created from JSON.")

        # This could be extended if you have more engine types
        if self.model_definition.dynamics.typology == ModelTypes.DIFFERENCE_EQUATIONS:
            logging.info("Initializing DifferenceEquations engine.")
            return difference.DifferenceEquations(rust_model_instance)

        raise NotImplementedError(
            (
                f"Engine for typology '{self.model_definition.dynamics.typology}' "
                f"not implemented."
            )
        )

    def _run_raw(self, num_steps: int) -> list[list[float]]:
        """
        Runs the simulation and returns the raw, high-performance output.
        This is the fastest method, returning a list of lists of floats.
        """
        logging.info(f"Running raw simulation for {num_steps} steps.")
        start = time.time()
        results = self._engine.run(num_steps)
        end = time.time()
        logging.info(f"Raw simulation complete. It tool {end - start} seconds.")
        return results

    @overload
    def run(
        self, num_steps: int, output_format: Literal["list_of_lists"]
    ) -> list[list[float]]: ...
    @overload
    def run(
        self, num_steps: int, output_format: Literal["dict_of_lists"]
    ) -> dict[str, list[float]]: ...
    @overload
    def run(self, num_steps: int) -> dict[str, list[float]]: ...
    def run(
        self,
        num_steps: int,
        output_format: Literal["dict_of_lists", "list_of_lists"] = "dict_of_lists",
    ) -> dict[str, list[float]] | list[list[float]]:
        """
        Runs the simulation and returns the output in the specified format.

        Parameters
        ----------
        num_steps : int
            The number of steps for the simulation.
        output_format : {'dict_of_lists', 'list_of_lists'}, default 'dict_of_lists'
            - 'dict_of_lists': Returns a dictionary of lists, with compartment names
                as keys.
            - 'list_of_lists': Returns a list of lists of floats, where the first level
                is the step and the second level the comptarment.

        Returns
        -------
        dict[str, list[float]] | list[list[float]]
            The simulation results in the specified format.
        """
        raw_results = self._run_raw(num_steps)
        if output_format == "list_of_lists":
            logging.info("Returning results in 'list_of_lists' format.")
            return raw_results

        elif output_format == "dict_of_lists":
            logging.info("Transposing raw results to 'dict_of_lists' format.")
            if not raw_results:
                return {c: [] for c in self._compartments}
            transposed_results = zip(*raw_results)
            return {
                compartment: list(values)
                for compartment, values in zip(self._compartments, transposed_results)
            }

        else:
            assert_never(output_format)

    @property
    def engine(self) -> "DifferenceEquationsProtocol":
        """Get the underlying simulation engine."""
        return self._engine

Attributes

engine property

engine: DifferenceEquationsProtocol

Get the underlying simulation engine.

Functions

__init__

__init__(model: Model)

Initializes the simulation engine from a Pydantic Model definition.

Parameters:

Name Type Description Default
model Model

A fully constructed and validated model object.

required
Source code in commol/api/simulation.py
def __init__(self, model: Model):
    """
    Initializes the simulation engine from a Pydantic Model definition.

    Parameters
    ----------
    model : Model
        A fully constructed and validated model object.
    """
    logging.info(f"Initializing Simulation with model: '{model.name}'")
    self.model_definition: Model = model
    self._engine: "DifferenceEquationsProtocol" = self._initialize_engine()

    self._compartments: list[str] = self._engine.compartments
    logging.info(
        f"Simulation engine ready. Total compartments: {len(self._compartments)}"
    )

run

run(num_steps: int, output_format: Literal['list_of_lists']) -> list[list[float]]
run(num_steps: int, output_format: Literal['dict_of_lists']) -> dict[str, list[float]]
run(num_steps: int) -> dict[str, list[float]]
run(num_steps: int, output_format: Literal['dict_of_lists', 'list_of_lists'] = 'dict_of_lists') -> dict[str, list[float]] | list[list[float]]

Runs the simulation and returns the output in the specified format.

Parameters:

Name Type Description Default
num_steps int

The number of steps for the simulation.

required
output_format (dict_of_lists, list_of_lists)
  • 'dict_of_lists': Returns a dictionary of lists, with compartment names as keys.
  • 'list_of_lists': Returns a list of lists of floats, where the first level is the step and the second level the comptarment.
'dict_of_lists'

Returns:

Type Description
dict[str, list[float]] | list[list[float]]

The simulation results in the specified format.

Source code in commol/api/simulation.py
def run(
    self,
    num_steps: int,
    output_format: Literal["dict_of_lists", "list_of_lists"] = "dict_of_lists",
) -> dict[str, list[float]] | list[list[float]]:
    """
    Runs the simulation and returns the output in the specified format.

    Parameters
    ----------
    num_steps : int
        The number of steps for the simulation.
    output_format : {'dict_of_lists', 'list_of_lists'}, default 'dict_of_lists'
        - 'dict_of_lists': Returns a dictionary of lists, with compartment names
            as keys.
        - 'list_of_lists': Returns a list of lists of floats, where the first level
            is the step and the second level the comptarment.

    Returns
    -------
    dict[str, list[float]] | list[list[float]]
        The simulation results in the specified format.
    """
    raw_results = self._run_raw(num_steps)
    if output_format == "list_of_lists":
        logging.info("Returning results in 'list_of_lists' format.")
        return raw_results

    elif output_format == "dict_of_lists":
        logging.info("Transposing raw results to 'dict_of_lists' format.")
        if not raw_results:
            return {c: [] for c in self._compartments}
        transposed_results = zip(*raw_results)
        return {
            compartment: list(values)
            for compartment, values in zip(self._compartments, transposed_results)
        }

    else:
        assert_never(output_format)

options: show_root_heading: true show_source: true heading_level: 2 show_docstring_attributes: false