-
Notifications
You must be signed in to change notification settings - Fork 211
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: introduce global configuration instance (#758)
- Loading branch information
1 parent
757b961
commit df9c561
Showing
11 changed files
with
630 additions
and
27 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,93 @@ | ||
# -*- coding: utf-8 -*- | ||
|
||
"""Define the global configuration.""" | ||
|
||
from __future__ import absolute_import | ||
|
||
import types | ||
|
||
from six import with_metaclass, string_types | ||
|
||
from cobra.exceptions import SolverNotFound | ||
from cobra.core.singleton import Singleton | ||
from cobra.util.solver import interface_to_str, solvers | ||
|
||
|
||
__all__ = ("Configuration",) | ||
|
||
|
||
class BaseConfiguration(object): | ||
""" | ||
Define global configuration value that will be honored by cobra functions. | ||
This object sets default values for the modifiable attributes like | ||
default solver, reaction bounds etc. | ||
Attributes | ||
---------- | ||
solver : {"glpk", "cplex", "gurobi"} | ||
The default solver for new models. The solver choices are the ones | ||
provided by `optlang` and solvers installed in your environment. | ||
lower_bound : float | ||
The standard lower bound for reversible reactions (default -1000). | ||
upper_bound : float | ||
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). | ||
""" | ||
|
||
def __init__(self): | ||
self._solver = None | ||
self.lower_bound = None | ||
self.upper_bound = None | ||
# Set the default solver from a preferred order. | ||
for name in ["gurobi", "cplex", "glpk"]: | ||
try: | ||
self.solver = name | ||
except SolverNotFound: | ||
continue | ||
else: | ||
break | ||
self.bounds = -1000.0, 1000.0 | ||
|
||
@property | ||
def solver(self): | ||
return self._solver | ||
|
||
@solver.setter | ||
def solver(self, value): | ||
not_valid_interface = SolverNotFound( | ||
"'{}' is not a valid solver interface. Pick one from {}.".format( | ||
value, ", ".join(list(solvers)))) | ||
if isinstance(value, string_types): | ||
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 | ||
def bounds(self): | ||
return self.lower_bound, self.upper_bound | ||
|
||
@bounds.setter | ||
def bounds(self, bounds): | ||
# TODO: We should consider allowing `None` for free bounds. | ||
assert bounds[0] <= bounds[1] | ||
self.lower_bound = bounds[0] | ||
self.upper_bound = bounds[1] | ||
|
||
def __repr__(self): | ||
return "solver: {}\nlower_bound: {}\nupper_bound: {}".format( | ||
interface_to_str(self.solver), self.lower_bound, self.upper_bound) | ||
|
||
|
||
class Configuration(with_metaclass(Singleton, BaseConfiguration)): | ||
"""Define the configuration to be singleton based.""" | ||
|
||
pass |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
# -*- coding: utf-8 -*- | ||
|
||
"""Define the singleton meta class.""" | ||
|
||
from __future__ import absolute_import | ||
|
||
|
||
class Singleton(type): | ||
"""Implementation of the singleton pattern as a meta class.""" | ||
|
||
_instances = {} | ||
|
||
def __call__(cls, *args, **kwargs): | ||
"""Override an inheriting class' call.""" | ||
if cls not in cls._instances: | ||
cls._instances[cls] = super(Singleton, cls).__call__(*args, | ||
**kwargs) | ||
return cls._instances[cls] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
# -*- coding: utf-8 -*- | ||
|
||
"""Test functions of configuration.py""" | ||
|
||
from __future__ import absolute_import | ||
|
||
from cobra.core import Configuration | ||
from cobra.util.solver import interface_to_str | ||
|
||
|
||
def test_default_bounds(): | ||
"""Verify the default bounds.""" | ||
config = Configuration() | ||
assert config.bounds == (-1000.0, 1000.0) | ||
|
||
|
||
def test_bounds(): | ||
"""Test changing bounds.""" | ||
config = Configuration() | ||
config.bounds = 100.0, 10000.0 | ||
assert config.bounds == (100.0, 10000.0) | ||
# Restore default values. | ||
config.bounds = -1000.0, 1000.0 | ||
|
||
|
||
def test_solver(): | ||
"""Test assignment of different solvers.""" | ||
config = Configuration() | ||
config.solver = "glpk" | ||
assert interface_to_str(config.solver) == "glpk" | ||
config.solver = "glpk_exact" | ||
assert interface_to_str(config.solver) == "glpk_exact" | ||
# Restore default solver. | ||
config.solver = "glpk" |
Oops, something went wrong.