From c4a8bdecc954066c291718674a155345d8857df1 Mon Sep 17 00:00:00 2001 From: "Pablo R. Mier" Date: Sat, 21 Aug 2021 17:40:55 +0200 Subject: [PATCH 1/4] Update README.md [skip ci] --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 699b9cc..8132481 100644 --- a/README.md +++ b/README.md @@ -6,8 +6,9 @@
[![Try It Online](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/drive/1JAOEHLlRCW8GziIpBqkFwJL2ha3OEOWJ?usp=sharing) -[![PyPI](https://img.shields.io/pypi/v/miom?style=flat-square&label=PyPI&logo=pypi&logoColor=white)](https://pypi.org/project/miom/) +[![PyPI](https://img.shields.io/pypi/v/miom?label=PyPI)](https://pypi.org/project/miom/) ![Tests](https://github.com/metexplore/miom/actions/workflows/unit-tests.yml/badge.svg) +[![Downloads](https://pepy.tech/badge/miom)](https://pepy.tech/project/miom)
From 716343cbb530540c6448d2106b909f6f4fa424be Mon Sep 17 00:00:00 2001 From: "Pablo R. Mier" Date: Sat, 21 Aug 2021 17:43:52 +0200 Subject: [PATCH 2/4] Update README.md [skip ci] --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 8132481..6c361bb 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@
[![Try It Online](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/drive/1JAOEHLlRCW8GziIpBqkFwJL2ha3OEOWJ?usp=sharing) -[![PyPI](https://img.shields.io/pypi/v/miom?label=PyPI)](https://pypi.org/project/miom/) +[![PyPI](https://img.shields.io/pypi/v/miom?label=PyPI&logo=pypi&logoColor=lightgrey)](https://pypi.org/project/miom/) ![Tests](https://github.com/metexplore/miom/actions/workflows/unit-tests.yml/badge.svg) [![Downloads](https://pepy.tech/badge/miom)](https://pepy.tech/project/miom) From 467c72f4cecb48d09a07f29e701148405663efd9 Mon Sep 17 00:00:00 2001 From: "Pablo R. Mier" Date: Sun, 22 Aug 2021 20:55:33 +0200 Subject: [PATCH 3/4] Improve PICOS performance Use matrix notation for subset_selection problem. It greatly improves both speed and memory consumption during the creation of the problem. This fixes #22 --- docs/index.md | 3 ++- miom/miom.py | 59 ++++++++++++++++++++++++++++++++++++++------------- 2 files changed, 46 insertions(+), 16 deletions(-) diff --git a/docs/index.md b/docs/index.md index 065962c..8e3ac9d 100644 --- a/docs/index.md +++ b/docs/index.md @@ -1,8 +1,9 @@ MIOM [![Try It Online](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/drive/1JAOEHLlRCW8GziIpBqkFwJL2ha3OEOWJ?usp=sharing) -[![PyPI](https://img.shields.io/pypi/v/miom?style=flat-square&label=PyPI&logo=pypi&logoColor=white)](https://pypi.org/project/miom/) +[![PyPI](https://img.shields.io/pypi/v/miom?label=PyPI)](https://pypi.org/project/miom/) ![Tests](https://github.com/metexplore/miom/actions/workflows/unit-tests.yml/badge.svg) +[![Downloads](https://pepy.tech/badge/miom)](https://pepy.tech/project/miom) # Introduction diff --git a/miom/miom.py b/miom/miom.py index 3756ed9..730373d 100644 --- a/miom/miom.py +++ b/miom/miom.py @@ -148,14 +148,25 @@ class Comparator(str, Enum): ("type", _ReactionType) ]) -def _get_reversible_vars(weighted_rxn): +def _get_reversible_vars(weighted_rxns): d = defaultdict(list) - for i, rxn in enumerate(weighted_rxn): + for i, rxn in enumerate(weighted_rxns): if rxn.type == _ReactionType.RH_POS or rxn.type == _ReactionType.RH_NEG: d[rxn.index].append(i) return [l for l in d.values() if len(l) > 1] +def _get_rxn_var_data(weighted_rxns, rxn_type): + idx = [ + (i, rd.index, rd.lb, rd.ub) + for i, rd in enumerate(weighted_rxns) + if rd.type is rxn_type + ] + if len(idx) > 0: + return [np.array(x) for x in list(zip(*idx))] + return None + + def load(network, solver=Solvers.COIN_OR_CBC): """ Create a MIOM optimization model for a given solver. @@ -857,20 +868,38 @@ def _subset_selection(self, *args, **kwargs): P = self.problem # Build the MIP problem V = self.variables.fluxvars - X = pc.BinaryVariable("X", shape=(len(weighted_rxns), 1)) + X = pc.BinaryVariable("X", shape=(len(weighted_rxns),)) C = pc.Constant("C", value=np.array([abs(rxn.cost) for rxn in weighted_rxns])) - for i, rd in enumerate(weighted_rxns): - if rd.type is _ReactionType.RH_POS: - P.add_constraint(V[rd.index] >= X[i] * (eps - rd.lb) + rd.lb) - elif rd.type is _ReactionType.RH_NEG: - P.add_constraint(V[rd.index] <= rd.ub - X[i] * (rd.ub + eps)) - elif rd.type is _ReactionType.RL: - P.add_constraint((1 - X[i]) * rd.ub - V[rd.index] >= 0) - P.add_constraint((1 - X[i]) * rd.lb - V[rd.index] <= 0) - else: - raise ValueError("Unknown type of reaction") - for i, j in _get_reversible_vars(weighted_rxns): - self.add_constraint(X[i] + X[j] <= 1) + # NOTE: For some reason, PICOS have issues parsing the priority operators, so constraints + # are split into many parts, otherwise it does not work. The matrix notation is more + # efficient and takes less time and memory than using the add_list_of_constraints method in a loop. + var_data = _get_rxn_var_data(weighted_rxns, _ReactionType.RH_POS) + if var_data is not None: + I, J, LB, UB = var_data + EA = eps - LB + EB = X[I]^EA + expr = V[J] >= EB + LB + P.add_constraint(expr) + var_data = _get_rxn_var_data(weighted_rxns, _ReactionType.RH_NEG) + if var_data is not None: + I, J, LB, UB = var_data + EA = eps + UB + EB = X[I]^EA + expr = V[J] <= UB - EB + P.add_constraint(expr) + var_data = _get_rxn_var_data(weighted_rxns, _ReactionType.RL) + if var_data is not None: + I, J, LB, UB = var_data + EA = 1 - X[I] + EB = EA^UB + EC = EA^LB + P.add_constraint(EB - V[J] >= 0) + P.add_constraint(EC - V[J] <= 0) + # Strengthen the formulation + idx = [np.array(x) for x in list(zip(*_get_reversible_vars(weighted_rxns)))] + if len(idx) > 0: + I, J = idx + P.add_constraint(X[I] + X[J] <= 1) P.set_objective(kwargs['direction'], C.T * X) self.variables._indicator_vars = X return True From aa6a412d205c06bbd76cb60168cf690951807256 Mon Sep 17 00:00:00 2001 From: "Pablo R. Mier" Date: Sun, 22 Aug 2021 20:56:30 +0200 Subject: [PATCH 4/4] Bump version [skip ci] --- miom/__init__.py | 2 +- pyproject.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/miom/__init__.py b/miom/__init__.py index ba046b9..1acae27 100644 --- a/miom/__init__.py +++ b/miom/__init__.py @@ -8,4 +8,4 @@ from miom.mio import load_gem __all__ = ["load", "load_gem", "Solvers", "Comparator", "ExtractionMode"] -__version__ = "0.9.0-beta.5" \ No newline at end of file +__version__ = "0.9.0-beta.6" \ No newline at end of file diff --git a/pyproject.toml b/pyproject.toml index 90f069a..27c9b20 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "miom" -version = "0.9.0-beta.5" +version = "0.9.0-beta.6" description = "Mixed Integer Optimization for Metabolism" authors = ["Pablo R. Mier "] keywords = ["optimization", "LP", "MIP", "metabolism", "metabolic-networks"]