From 991cd74c5d7d1ccbac25728e017db53799fd4d67 Mon Sep 17 00:00:00 2001 From: Josh Loecker Date: Wed, 23 Oct 2024 18:16:40 -0500 Subject: [PATCH 01/17] Added pyproject with minimum dependencies --- pyproject.toml | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 pyproject.toml diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..2561be0 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,11 @@ +[project] +name = "troppo" +version = "0.1.0" +description = "Add your description here" +readme = "README.rst" +requires-python = ">=3.9" +dependencies = [ + "cobamp>=0.2.1", + "cobra>=0.24.0", + "xlrd>=1.2.0" +] From c423b5e3bf1b2e6c53e9eac0230e2195d6f856e7 Mon Sep 17 00:00:00 2001 From: Josh Loecker Date: Fri, 25 Oct 2024 12:12:43 -0500 Subject: [PATCH 02/17] fix: pyproject for installation setup --- pyproject.toml | 32 +++++++++++++++++++++++++++++--- 1 file changed, 29 insertions(+), 3 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 2561be0..1a4fd32 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,11 +1,37 @@ [project] name = "troppo" version = "0.1.0" -description = "Add your description here" +description = "Constraint-based modeling framework for the enumeration of pathway analysis concepts" +authors = [ + {name = "Jorge Ferreira", email = "jorge.ferreira@ceb.uminho.pt"}, + {name = "Vítor Vieira"} +] readme = "README.rst" +license = {file = "LICENSE"} +homepage = "https://github.com/BioSystemsUM/troppo" requires-python = ">=3.9" -dependencies = [ +classifiers = [ + "Development Status :: 4 - Beta", + "Topic :: Scientific/Engineering :: Bio-Informatics", + "Intended Audience :: Science/Research", + "Programming Language :: Python :: 3.5", + "License :: OSI Approved :: GNU General Public License v3 (GPLv3)", + "'Topic :: Software Development :: Libraries :: Python Modules"' +] + + +[project.urls] +Documentation = "https://github.com/BioSystemsUM/troppo/blob/main/README.rst" + +[build-system] +requires = ["setuptools>=61.0"] +build-backend = "setuptools.build_meta" + +[tool.setuptools] +package-dir = { "" = "src" } +packages = { find = "src" } +install_requires = [ "cobamp>=0.2.1", "cobra>=0.24.0", "xlrd>=1.2.0" -] +] \ No newline at end of file From f118f591e0395e1b49537f1371b520e447e44ab5 Mon Sep 17 00:00:00 2001 From: Josh Loecker Date: Fri, 25 Oct 2024 12:14:14 -0500 Subject: [PATCH 03/17] fix: quotations --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 1a4fd32..b70c6ef 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -16,7 +16,7 @@ classifiers = [ "Intended Audience :: Science/Research", "Programming Language :: Python :: 3.5", "License :: OSI Approved :: GNU General Public License v3 (GPLv3)", - "'Topic :: Software Development :: Libraries :: Python Modules"' + "'Topic :: Software Development :: Libraries :: Python Modules" ] From 9a5dd1af9081abfda2daadd0e5544e4932a70127 Mon Sep 17 00:00:00 2001 From: Josh Loecker Date: Fri, 25 Oct 2024 12:15:49 -0500 Subject: [PATCH 04/17] fix: project urls --- pyproject.toml | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index b70c6ef..d0be769 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -8,7 +8,6 @@ authors = [ ] readme = "README.rst" license = {file = "LICENSE"} -homepage = "https://github.com/BioSystemsUM/troppo" requires-python = ">=3.9" classifiers = [ "Development Status :: 4 - Beta", @@ -21,7 +20,10 @@ classifiers = [ [project.urls] -Documentation = "https://github.com/BioSystemsUM/troppo/blob/main/README.rst" +Homepage = "https://github.com/BioSystemsUM/troppo" +Documentation = "http://troppo-bisbi.readthedocs.io/" +Repository = "https://github.com/BioSystemsUM/troppo/" +Issues = "https://github.com/BioSystemsUM/troppo/issues" [build-system] requires = ["setuptools>=61.0"] From 7dd6b8b7f07899e4d3a4767624f89d5187dbe129 Mon Sep 17 00:00:00 2001 From: Josh Loecker Date: Fri, 25 Oct 2024 12:17:32 -0500 Subject: [PATCH 05/17] revert: include dependencies under `project` --- pyproject.toml | 18 +++++++----------- 1 file changed, 7 insertions(+), 11 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index d0be769..e03e1bb 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -9,6 +9,11 @@ authors = [ readme = "README.rst" license = {file = "LICENSE"} requires-python = ">=3.9" +dependencies = [ + "cobamp>=0.2.1", + "cobra>=0.24.0", + "xlrd>=1.2.0" +] classifiers = [ "Development Status :: 4 - Beta", "Topic :: Scientific/Engineering :: Bio-Informatics", @@ -26,14 +31,5 @@ Repository = "https://github.com/BioSystemsUM/troppo/" Issues = "https://github.com/BioSystemsUM/troppo/issues" [build-system] -requires = ["setuptools>=61.0"] -build-backend = "setuptools.build_meta" - -[tool.setuptools] -package-dir = { "" = "src" } -packages = { find = "src" } -install_requires = [ - "cobamp>=0.2.1", - "cobra>=0.24.0", - "xlrd>=1.2.0" -] \ No newline at end of file +requires = ["hatchling"] +build-backend = "hatchling.build" From 8fe364c80d39568af2801ef52464b1ab7cdfd5e9 Mon Sep 17 00:00:00 2001 From: Josh Loecker Date: Fri, 25 Oct 2024 12:18:14 -0500 Subject: [PATCH 06/17] fix: quotations --- pyproject.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index e03e1bb..9dc9881 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -17,10 +17,10 @@ dependencies = [ classifiers = [ "Development Status :: 4 - Beta", "Topic :: Scientific/Engineering :: Bio-Informatics", + "Topic :: Software Development :: Libraries :: Python Modules", "Intended Audience :: Science/Research", "Programming Language :: Python :: 3.5", - "License :: OSI Approved :: GNU General Public License v3 (GPLv3)", - "'Topic :: Software Development :: Libraries :: Python Modules" + "License :: OSI Approved :: GNU General Public License v3 (GPLv3)" ] From 9b53b71c9ef55de3e11205ded81d4e9de1619074 Mon Sep 17 00:00:00 2001 From: Josh Loecker Date: Fri, 25 Oct 2024 12:18:32 -0500 Subject: [PATCH 07/17] revert: license location --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 9dc9881..dd7ed6a 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -7,7 +7,7 @@ authors = [ {name = "Vítor Vieira"} ] readme = "README.rst" -license = {file = "LICENSE"} +license = {file = "LICENSE.txt"} requires-python = ">=3.9" dependencies = [ "cobamp>=0.2.1", From 3b7302417d32e2fadaee356cc49e8c182a7dd531 Mon Sep 17 00:00:00 2001 From: Josh Loecker Date: Fri, 25 Oct 2024 12:20:48 -0500 Subject: [PATCH 08/17] Fix description --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index dd7ed6a..c55df5f 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,7 +1,7 @@ [project] name = "troppo" version = "0.1.0" -description = "Constraint-based modeling framework for the enumeration of pathway analysis concepts" +description = "Reconstruction algorithms for Python" authors = [ {name = "Jorge Ferreira", email = "jorge.ferreira@ceb.uminho.pt"}, {name = "Vítor Vieira"} From 45b15b4719f7713e591a332657533ec7e4678b5b Mon Sep 17 00:00:00 2001 From: Josh Loecker Date: Wed, 11 Dec 2024 11:47:48 -0600 Subject: [PATCH 09/17] feat: allow setting solver --- src/troppo/methods/base.py | 2 +- src/troppo/methods/reconstruction/imat.py | 6 ++++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/troppo/methods/base.py b/src/troppo/methods/base.py index 1ca219c..f29fc3a 100644 --- a/src/troppo/methods/base.py +++ b/src/troppo/methods/base.py @@ -14,7 +14,7 @@ class ContextSpecificModelReconstructionAlgorithm(object): __metaclass__ = abc.ABCMeta @abc.abstractmethod - def __init__(self, S, lb, ub, properties): + def __init__(self, S, lb, ub, properties, solver): pass @abc.abstractmethod diff --git a/src/troppo/methods/reconstruction/imat.py b/src/troppo/methods/reconstruction/imat.py index 07fee06..d5fc3be 100644 --- a/src/troppo/methods/reconstruction/imat.py +++ b/src/troppo/methods/reconstruction/imat.py @@ -26,7 +26,7 @@ class IMATProperties(PropertiesReconstruction): The epsilon, by default 1 """ def __init__(self, exp_vector: np.ndarray or list, exp_thresholds: tuple or list or ndarray, - core: ndarray or list or tuple = None, tolerance: float = 1e-8, epsilon: int or float = 1): + core: ndarray or list or tuple = None, tolerance: float = 1e-8, epsilon: int or float = 1, solver: str = None): new_mandatory = { 'exp_vector': lambda x: isinstance(x, list) and len(x) > 0 or isinstance(x, ndarray), 'exp_thresholds': lambda x: type(x) in (tuple, list, ndarray) and type(x[0]) in [float, int] and type( @@ -49,6 +49,8 @@ def __init__(self, exp_vector: np.ndarray or list, exp_thresholds: tuple or list self['tolerance'] = tolerance if epsilon: self['epsilon'] = epsilon + if solver: + self["solver"] = solver @staticmethod def from_integrated_scores(scores: list, **kwargs) -> 'IMATProperties': @@ -213,7 +215,7 @@ def generate_imat_problem(self, S, lb, ub, high_idx, low_idx, epsilon): prefix_maker = lambda cd: list([cd[0] + str(i) for i in range(cd[1])]) A_names = list(chain(*list(map(prefix_maker, [('V', n), ('Hpos', nh), ('Hneg', nh), ('L', nl)])))) - lsystem = GenericLinearSystem(S=A, var_types=A_vt, lb=A_lb, ub=A_ub, b_lb=b_lb, b_ub=b_ub, var_names=A_names) + lsystem = GenericLinearSystem(S=A, var_types=A_vt, lb=A_lb, ub=A_ub, b_lb=b_lb, b_ub=b_ub, var_names=A_names, solver=self.properties["solver"]) lso = LinearSystemOptimizer(lsystem) A_f = np.zeros((A.shape[1])) From b6ba44ea9107684be0961713d8b1b7f5cb17baa1 Mon Sep 17 00:00:00 2001 From: Josh Loecker Date: Wed, 11 Dec 2024 11:57:31 -0600 Subject: [PATCH 10/17] fix: set solver in iMATProperties class Signed-off-by: Josh Loecker --- src/troppo/methods/base.py | 2 +- src/troppo/methods/reconstruction/imat.py | 5 ++--- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/src/troppo/methods/base.py b/src/troppo/methods/base.py index f29fc3a..1ca219c 100644 --- a/src/troppo/methods/base.py +++ b/src/troppo/methods/base.py @@ -14,7 +14,7 @@ class ContextSpecificModelReconstructionAlgorithm(object): __metaclass__ = abc.ABCMeta @abc.abstractmethod - def __init__(self, S, lb, ub, properties, solver): + def __init__(self, S, lb, ub, properties): pass @abc.abstractmethod diff --git a/src/troppo/methods/reconstruction/imat.py b/src/troppo/methods/reconstruction/imat.py index d5fc3be..cf10f92 100644 --- a/src/troppo/methods/reconstruction/imat.py +++ b/src/troppo/methods/reconstruction/imat.py @@ -30,7 +30,8 @@ def __init__(self, exp_vector: np.ndarray or list, exp_thresholds: tuple or list new_mandatory = { 'exp_vector': lambda x: isinstance(x, list) and len(x) > 0 or isinstance(x, ndarray), 'exp_thresholds': lambda x: type(x) in (tuple, list, ndarray) and type(x[0]) in [float, int] and type( - x[1]) in [float, int] + x[1]) in [float, int], + "solver": solver } new_optional = { 'core': lambda x: type(x) in [ndarray, list, tuple], @@ -49,8 +50,6 @@ def __init__(self, exp_vector: np.ndarray or list, exp_thresholds: tuple or list self['tolerance'] = tolerance if epsilon: self['epsilon'] = epsilon - if solver: - self["solver"] = solver @staticmethod def from_integrated_scores(scores: list, **kwargs) -> 'IMATProperties': From 2ad0936a79f1d75494b8d6c5cda74de4349a6168 Mon Sep 17 00:00:00 2001 From: Josh Loecker Date: Wed, 1 Oct 2025 11:52:22 -0500 Subject: [PATCH 11/17] style(ruff): automatic formatting with ruff --- src/troppo/methods/reconstruction/gimme.py | 104 ++++++++++++--------- 1 file changed, 59 insertions(+), 45 deletions(-) diff --git a/src/troppo/methods/reconstruction/gimme.py b/src/troppo/methods/reconstruction/gimme.py index 2068292..7998304 100644 --- a/src/troppo/methods/reconstruction/gimme.py +++ b/src/troppo/methods/reconstruction/gimme.py @@ -1,9 +1,10 @@ -import numpy as np from collections import OrderedDict -from numpy import ndarray, array +import numpy as np from cobamp.core.models import ConstraintBasedModel from cobamp.core.optimization import Solution +from numpy import array, ndarray + from troppo.methods.base import ContextSpecificModelReconstructionAlgorithm, PropertiesReconstruction @@ -26,6 +27,7 @@ class GIMMEModel(ConstraintBasedModel): A dictionary that maps the reactions of the template model to the reactions of the GIMME model. """ + def __init__(self, cbmodel: ConstraintBasedModel, solver: str or None = None): self.cbmodel = cbmodel if not self.cbmodel.model: @@ -35,13 +37,12 @@ def __init__(self, cbmodel: ConstraintBasedModel, solver: str or None = None): S = irrev_model.get_stoichiometric_matrix() bounds = irrev_model.bounds - super().__init__(S, bounds, irrev_model.reaction_names, irrev_model.metabolite_names, solver=solver, - optimizer=True) + super().__init__(S, bounds, irrev_model.reaction_names, irrev_model.metabolite_names, solver=solver, optimizer=True) def __adjust_objective_to_irreversible(self, objective_dict): obj_dict = {} for k, v in objective_dict.items(): - irrev_map = self.mapping[self.cbmodel.decode_index(k, 'reaction')] + irrev_map = self.mapping[self.cbmodel.decode_index(k, "reaction")] if isinstance(irrev_map, (list, tuple)): for i in irrev_map: obj_dict[i] = v @@ -50,7 +51,9 @@ def __adjust_objective_to_irreversible(self, objective_dict): return obj_dict def __adjust_expression_vector_to_irreversible(self, exp_vector): - exp_vector_n = np.zeros(len(self.reaction_names), ) + exp_vector_n = np.zeros( + len(self.reaction_names), + ) for rxn, val in enumerate(exp_vector): rxmap = self.mapping[rxn] if isinstance(rxmap, tuple): @@ -59,8 +62,7 @@ def __adjust_expression_vector_to_irreversible(self, exp_vector): exp_vector_n[rxmap] = val return exp_vector_n - def optimize_gimme(self, exp_vector: list, objectives: list or tuple, obj_frac: list or tuple or float = 0.9, - flux_thres: float = None): + def optimize_gimme(self, exp_vector: list, objectives: list or tuple, obj_frac: list or tuple or float = 0.9, flux_thres: float = None): """ Optimize the GIMME model. @@ -91,8 +93,7 @@ def find_objective_value(obj): objective_values = list(map(find_objective_value, objectives_irr)) - gimme_model_objective = array( - [flux_thres - exp_vector_irr[i] if -1 < exp_vector_irr[i] < flux_thres else 0 for i in range(N)]) + gimme_model_objective = array([flux_thres - exp_vector_irr[i] if -1 < exp_vector_irr[i] < flux_thres else 0 for i in range(N)]) objective_lbs = np.zeros(len(self.reaction_names)) for ov, obj in zip(objective_values, objectives_irr): @@ -126,17 +127,16 @@ class GIMMESolution(Solution): A dictionary that maps the reactions of the template model to the reactions of the GIMME model. """ + def __init__(self, sol, exp_vector, var_names, mapping=None): self.exp_vector = exp_vector gimme_solution = sol.x() if mapping: - gimme_solution = [max(gimme_solution[array(new)]) if isinstance(new, (tuple, list)) else gimme_solution[new] - for orig, new - in mapping.items()] + gimme_solution = [ + max(gimme_solution[array(new)]) if isinstance(new, (tuple, list)) else gimme_solution[new] for orig, new in mapping.items() + ] super().__init__( - value_map=OrderedDict([(k, v) for k, v in zip(var_names, gimme_solution)]), - status=sol.status(), - objective_value=sol.objective_value() + value_map=OrderedDict([(k, v) for k, v in zip(var_names, gimme_solution)]), status=sol.status(), objective_value=sol.objective_value() ) def get_reaction_activity(self, flux_threshold: float): @@ -188,31 +188,43 @@ class GIMMEProperties(PropertiesReconstruction): List of metabolite ids """ - def __init__(self, exp_vector: list, objectives: list or tuple, obj_frac: list or tuple or float = 0.9, - preprocess: bool = False, flux_threshold: float = None, solver: str = None, reaction_ids: list = None, - metabolite_ids: list = None): + + def __init__( + self, + exp_vector: list, + objectives: list or tuple, + obj_frac: list or tuple or float = 0.9, + preprocess: bool = False, + flux_threshold: float = None, + solver: str = None, + reaction_ids: list = None, + metabolite_ids: list = None, + ): new_mandatory = { - 'exp_vector': lambda x: isinstance(x, list) and len(x) > 0 or isinstance(x, ndarray), - 'preprocess': lambda x: isinstance(x, bool) or x is None, - 'objectives': lambda x: type(x) in [list, tuple, ndarray], - 'reaction_ids': lambda x: isinstance(x, list) and len(x) > 0 or isinstance(x, ndarray), - 'metabolite_ids': lambda x: isinstance(x, list) and len(x) > 0 or isinstance(x, ndarray)} - - new_optional = {'obj_frac': lambda x: type(x) in [ndarray, list, tuple, float], - 'flux_threshold': lambda x: isinstance(x, float) or x is None, - 'solver': lambda x: isinstance(x, str) or x is None} + "exp_vector": lambda x: isinstance(x, list) and len(x) > 0 or isinstance(x, ndarray), + "preprocess": lambda x: isinstance(x, bool) or x is None, + "objectives": lambda x: type(x) in [list, tuple, ndarray], + "reaction_ids": lambda x: isinstance(x, list) and len(x) > 0 or isinstance(x, ndarray), + "metabolite_ids": lambda x: isinstance(x, list) and len(x) > 0 or isinstance(x, ndarray), + } + + new_optional = { + "obj_frac": lambda x: type(x) in [ndarray, list, tuple, float], + "flux_threshold": lambda x: isinstance(x, float) or x is None, + "solver": lambda x: isinstance(x, str) or x is None, + } super().__init__() self.add_new_properties(new_mandatory, new_optional) - self['objectives'] = objectives - self['exp_vector'] = exp_vector - self['solver'] = solver - self['reaction_ids'] = reaction_ids - self['metabolite_ids'] = metabolite_ids - self['obj_frac'] = obj_frac if isinstance(obj_frac, ndarray) else array([obj_frac] * len(objectives)) - self['preprocess'] = True if preprocess else False - self['flux_threshold'] = 1e-4 if flux_threshold is None else flux_threshold + self["objectives"] = objectives + self["exp_vector"] = exp_vector + self["solver"] = solver + self["reaction_ids"] = reaction_ids + self["metabolite_ids"] = metabolite_ids + self["obj_frac"] = obj_frac if isinstance(obj_frac, ndarray) else array([obj_frac] * len(objectives)) + self["preprocess"] = True if preprocess else False + self["flux_threshold"] = 1e-4 if flux_threshold is None else flux_threshold @staticmethod def from_integrated_scores(scores: list, **kwargs): @@ -231,7 +243,7 @@ def from_integrated_scores(scores: list, **kwargs): GIMMEProperties """ - return GIMMEProperties(exp_vector=scores, **{k: v for k, v in kwargs.items() if 'exp_vector' not in k}) + return GIMMEProperties(exp_vector=scores, **{k: v for k, v in kwargs.items() if "exp_vector" not in k}) class GIMME(ContextSpecificModelReconstructionAlgorithm): @@ -264,6 +276,7 @@ class GIMME(ContextSpecificModelReconstructionAlgorithm): gm: GIMMEModel GIMME model """ + properties_class = GIMMEProperties def __init__(self, S: list, lb: list, ub: list, properties: GIMMEProperties): @@ -273,9 +286,10 @@ def __init__(self, S: list, lb: list, ub: list, properties: GIMMEProperties): self.properties = properties self.model = GIMMEModel self.sol = None - cbm = ConstraintBasedModel(S, list(zip(lb, ub)), reaction_names=self.properties['reaction_ids'], - metabolite_names=self.properties['metabolite_ids']) - self.gm = GIMMEModel(cbm, self.properties['solver']) + cbm = ConstraintBasedModel( + S, list(zip(lb, ub)), reaction_names=self.properties["reaction_ids"], metabolite_names=self.properties["metabolite_ids"] + ) + self.gm = GIMMEModel(cbm, self.properties["solver"]) def run(self): """ @@ -287,10 +301,10 @@ def run(self): """ sol = self.gm.optimize_gimme( - exp_vector=self.properties['exp_vector'], - objectives=self.properties['objectives'], - obj_frac=self.properties['obj_frac'], - flux_thres=self.properties['flux_threshold'] + exp_vector=self.properties["exp_vector"], + objectives=self.properties["objectives"], + obj_frac=self.properties["obj_frac"], + flux_thres=self.properties["flux_threshold"], ) self.sol = sol - return sol.get_reaction_activity(self.properties['flux_threshold']) + return sol.get_reaction_activity(self.properties["flux_threshold"]) From fcc7120798e84c0da9b08aee5600a4148b2477d4 Mon Sep 17 00:00:00 2001 From: Josh Loecker Date: Wed, 1 Oct 2025 11:59:37 -0500 Subject: [PATCH 12/17] chore(dev): import types for use --- src/troppo/methods/reconstruction/gimme.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/troppo/methods/reconstruction/gimme.py b/src/troppo/methods/reconstruction/gimme.py index 7998304..18712d3 100644 --- a/src/troppo/methods/reconstruction/gimme.py +++ b/src/troppo/methods/reconstruction/gimme.py @@ -1,6 +1,8 @@ from collections import OrderedDict +from typing import Iterable, Mapping, Optional, Sequence, Union import numpy as np +import numpy.typing as npt from cobamp.core.models import ConstraintBasedModel from cobamp.core.optimization import Solution from numpy import array, ndarray From 362167513d087dc14cd51c9e82ed1f9139b7988e Mon Sep 17 00:00:00 2001 From: Josh Loecker Date: Wed, 1 Oct 2025 12:00:27 -0500 Subject: [PATCH 13/17] feat(dev): use Optional for optional types --- src/troppo/methods/reconstruction/gimme.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/troppo/methods/reconstruction/gimme.py b/src/troppo/methods/reconstruction/gimme.py index 18712d3..5f637d0 100644 --- a/src/troppo/methods/reconstruction/gimme.py +++ b/src/troppo/methods/reconstruction/gimme.py @@ -30,7 +30,7 @@ class GIMMEModel(ConstraintBasedModel): """ - def __init__(self, cbmodel: ConstraintBasedModel, solver: str or None = None): + def __init__(self, cbmodel: ConstraintBasedModel, solver: Optional[str] = None): self.cbmodel = cbmodel if not self.cbmodel.model: self.cbmodel.initialize_optimizer() @@ -65,6 +65,7 @@ def __adjust_expression_vector_to_irreversible(self, exp_vector): return exp_vector_n def optimize_gimme(self, exp_vector: list, objectives: list or tuple, obj_frac: list or tuple or float = 0.9, flux_thres: float = None): + flux_thres: Optional[float] = None, """ Optimize the GIMME model. @@ -197,10 +198,10 @@ def __init__( objectives: list or tuple, obj_frac: list or tuple or float = 0.9, preprocess: bool = False, - flux_threshold: float = None, - solver: str = None, - reaction_ids: list = None, - metabolite_ids: list = None, + flux_threshold: Optional[float] = None, + solver: Optional[str] = None, + reaction_ids: Optional[Iterable[str]] = None, + metabolite_ids: Optional[Iterable[str]] = None, ): new_mandatory = { "exp_vector": lambda x: isinstance(x, list) and len(x) > 0 or isinstance(x, ndarray), From e11f5fd17169253b2a286637c86be58292d13388 Mon Sep 17 00:00:00 2001 From: Josh Loecker Date: Wed, 1 Oct 2025 12:00:55 -0500 Subject: [PATCH 14/17] feat (dev): use generic types instead of list or tuple --- src/troppo/methods/reconstruction/gimme.py | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/src/troppo/methods/reconstruction/gimme.py b/src/troppo/methods/reconstruction/gimme.py index 5f637d0..df28961 100644 --- a/src/troppo/methods/reconstruction/gimme.py +++ b/src/troppo/methods/reconstruction/gimme.py @@ -64,8 +64,13 @@ def __adjust_expression_vector_to_irreversible(self, exp_vector): exp_vector_n[rxmap] = val return exp_vector_n - def optimize_gimme(self, exp_vector: list, objectives: list or tuple, obj_frac: list or tuple or float = 0.9, flux_thres: float = None): + def optimize_gimme( + self, + exp_vector: Iterable[float], + objectives: Iterable[Mapping[int, int]], + obj_frac: Union[Iterable[float], float] = 0.9, flux_thres: Optional[float] = None, + ): """ Optimize the GIMME model. @@ -74,7 +79,7 @@ def optimize_gimme(self, exp_vector: list, objectives: list or tuple, obj_frac: exp_vector: list A list of expression values for each reaction in the GIMME model. objectives: list or tuple - A list of dictionaries that define the objectives of the GIMME model. + A list of dictionaries where keys are reaction indices and values define the objectives of the GIMME model. obj_frac: list or tuple or float A list of fractions that define the lower bounds of the objectives. If a float is given, the same fraction is used for all objectives. @@ -194,9 +199,9 @@ class GIMMEProperties(PropertiesReconstruction): def __init__( self, - exp_vector: list, - objectives: list or tuple, - obj_frac: list or tuple or float = 0.9, + exp_vector: Iterable[float], + objectives: Sequence, + obj_frac: Union[Iterable[float], float] = 0.9, preprocess: bool = False, flux_threshold: Optional[float] = None, solver: Optional[str] = None, @@ -230,7 +235,7 @@ def __init__( self["flux_threshold"] = 1e-4 if flux_threshold is None else flux_threshold @staticmethod - def from_integrated_scores(scores: list, **kwargs): + def from_integrated_scores(scores: Iterable[float], **kwargs): """ Create GIMMEProperties from integrated scores From 411a1d1c2e7c91c4971ac927278a2ba50979142c Mon Sep 17 00:00:00 2001 From: Josh Loecker Date: Wed, 1 Oct 2025 12:01:06 -0500 Subject: [PATCH 15/17] feat(dev): raise error if required argument not given --- src/troppo/methods/reconstruction/gimme.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/troppo/methods/reconstruction/gimme.py b/src/troppo/methods/reconstruction/gimme.py index df28961..a4330e8 100644 --- a/src/troppo/methods/reconstruction/gimme.py +++ b/src/troppo/methods/reconstruction/gimme.py @@ -251,7 +251,12 @@ def from_integrated_scores(scores: Iterable[float], **kwargs): GIMMEProperties """ - return GIMMEProperties(exp_vector=scores, **{k: v for k, v in kwargs.items() if "exp_vector" not in k}) + if "objectives" not in kwargs: + raise ValueError("objectives must be provided") + kwargs.pop("exp_vector", None) + kwargs.pop("objectives", None) + + return GIMMEProperties(exp_vector=scores, objectives=kwargs["objectives"], **kwargs) class GIMME(ContextSpecificModelReconstructionAlgorithm): From 9ac84db911eecd117020a32e0e44473173b8b1c2 Mon Sep 17 00:00:00 2001 From: Josh Loecker Date: Wed, 1 Oct 2025 12:01:24 -0500 Subject: [PATCH 16/17] feat(dev): use `isinstance` for consistent runtime type checks --- src/troppo/methods/reconstruction/gimme.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/troppo/methods/reconstruction/gimme.py b/src/troppo/methods/reconstruction/gimme.py index a4330e8..9b9db3d 100644 --- a/src/troppo/methods/reconstruction/gimme.py +++ b/src/troppo/methods/reconstruction/gimme.py @@ -210,16 +210,16 @@ def __init__( ): new_mandatory = { "exp_vector": lambda x: isinstance(x, list) and len(x) > 0 or isinstance(x, ndarray), - "preprocess": lambda x: isinstance(x, bool) or x is None, - "objectives": lambda x: type(x) in [list, tuple, ndarray], + "preprocess": lambda x: isinstance(x, (bool, None)), + "objectives": lambda x: isinstance(x, (list, tuple, npt.NDArray)), "reaction_ids": lambda x: isinstance(x, list) and len(x) > 0 or isinstance(x, ndarray), "metabolite_ids": lambda x: isinstance(x, list) and len(x) > 0 or isinstance(x, ndarray), } new_optional = { - "obj_frac": lambda x: type(x) in [ndarray, list, tuple, float], - "flux_threshold": lambda x: isinstance(x, float) or x is None, - "solver": lambda x: isinstance(x, str) or x is None, + "obj_frac": lambda x: isinstance(x, (ndarray, list, tuple, float)), + "flux_threshold": lambda x: isinstance(x, (float, None)), + "solver": lambda x: isinstance(x, (str, None)), } super().__init__() From 43ea88ed1ba19dcb2959b68c0dca3f467bbdb323 Mon Sep 17 00:00:00 2001 From: Josh Loecker Date: Wed, 1 Oct 2025 12:05:19 -0500 Subject: [PATCH 17/17] chore(dev): require numpy >=1.20 --- pyproject.toml | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index c55df5f..a5b8b7d 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -3,16 +3,17 @@ name = "troppo" version = "0.1.0" description = "Reconstruction algorithms for Python" authors = [ - {name = "Jorge Ferreira", email = "jorge.ferreira@ceb.uminho.pt"}, - {name = "Vítor Vieira"} + { name = "Jorge Ferreira", email = "jorge.ferreira@ceb.uminho.pt" }, + { name = "Vítor Vieira" } ] readme = "README.rst" -license = {file = "LICENSE.txt"} +license = { file = "LICENSE.txt" } requires-python = ">=3.9" dependencies = [ "cobamp>=0.2.1", "cobra>=0.24.0", - "xlrd>=1.2.0" + "xlrd>=1.2.0", + "numpy>=1.20" ] classifiers = [ "Development Status :: 4 - Beta",