Source code for cobra.core.configuration

"""Provide a global configuration object."""


import logging
import types
from numbers import Number
from os import cpu_count
from textwrap import dedent
from typing import Optional, Tuple

from cobra.core.singleton import Singleton
from cobra.exceptions import SolverNotFound
from cobra.util.solver import interface_to_str
from cobra.util.solver import solvers as SOLVERS


__all__ = ("Configuration",)


logger = logging.getLogger(__name__)


[docs]class Configuration(metaclass=Singleton): """ Define a global configuration object. The attributes of this singleton object are used as default values by cobra functions. Attributes ---------- solver : {"glpk", "cplex", "gurobi", "glpk_exact"} The default solver for new models. The solver choices are the ones provided by `optlang` and depend on solvers installed in your environment. tolerance: float The default tolerance for the solver being used (default 1E-07). lower_bound : float, optional The standard lower bound for reversible reactions (default -1000). upper_bound : float, optional The standard upper bound for all reactions (default 1000). bounds : tuple of floats The default reaction bounds for newly created reactions. The bounds are in the form of lower_bound, upper_bound (default -1000.0, 1000.0). processes : int A default number of processes to use where multiprocessing is possible. The default number corresponds to the number of available cores (hyperthreads) minus one. """ def __init__(self, **kwargs) -> None: """Initialize the configuration with its default attribute values.""" super().__init__(**kwargs) self._solver = None self.tolerance = 1e-07 self.lower_bound = None self.upper_bound = None self.processes = None self.bounds = -1000.0, 1000.0 self._set_default_solver() self._set_default_processes()
[docs] def _set_default_solver(self) -> None: """Set the default solver from a preferred order.""" for name in ["gurobi", "cplex", "glpk"]: try: self.solver = name except SolverNotFound: continue else: break
[docs] def _set_default_processes(self) -> None: """Set the default number of processes.""" self.processes = cpu_count() if self.processes is None: logger.warning("The number of cores could not be detected - assuming one.") self.processes = 1 if self.processes > 1: self.processes -= 1
@property
[docs] def solver(self) -> types.ModuleType: """Return the optlang solver interface.""" return self._solver
@solver.setter def solver(self, value) -> None: """Set the optlang solver interface.""" not_valid_interface = SolverNotFound( f"'{value}' is not a valid solver interface. " f" Please pick one from {', '.join(SOLVERS)}." ) if isinstance(value, str): if value not in SOLVERS: raise not_valid_interface interface = SOLVERS[value] elif isinstance(value, types.ModuleType) and hasattr(value, "Model"): interface = value else: raise not_valid_interface self._solver = interface @property
[docs] def bounds(self) -> Tuple[Optional[Number], Optional[Number]]: """Return the lower, upper reaction bound pair.""" return self.lower_bound, self.upper_bound
@bounds.setter def bounds(self, bounds: Tuple[Optional[Number], Optional[Number]]) -> None: """Set the lower, upper reaction bound pair.""" if None not in bounds: assert bounds[0] <= bounds[1] self.lower_bound = bounds[0] self.upper_bound = bounds[1]
[docs] def __repr__(self) -> str: """Return a string representation of the current configuration values.""" return dedent( f""" solver: {interface_to_str(self.solver)} tolerance: {self.tolerance} lower_bound: {self.lower_bound} upper_bound: {self.upper_bound} processes: {self.processes} """
)
[docs] def _repr_html_(self) -> str: """ Return a rich HTML representation of the current configuration values. Notes ----- This special method is used automatically in Jupyter notebooks to display a result from a cell. """ return dedent( f""" <table> <thead> <tr> <td><strong>Attribute</strong></td> <td><strong>Description</strong></td> <td><strong>Value</strong></td> </tr> </thead> <tbody> <tr> <td><pre>solver</pre></td> <td>Mathematical optimization solver</td> <td>{interface_to_str(self.solver)}</td> </tr> <tr> <td><pre>tolerance</pre></td> <td>General solver tolerance (feasibility, integrality, etc.)</td> <td>{self.tolerance}</td> </tr> <tr> <td><pre>lower_bound</pre></td> <td>Default reaction lower bound</td> <td>{self.lower_bound}</td> </tr> <tr> <td><pre>upper_bound</pre></td> <td>Default reaction upper bound</td> <td>{self.upper_bound}</td> </tr> <tr> <td><pre>processes</pre></td> <td>Number of parallel processes</td> <td>{self.processes}</td> </tr> </tbody> </table> """
)