Skip to content

Commit

Permalink
Merge pull request #14 from MetExplore/development
Browse files Browse the repository at this point in the history
Prepare release of v0.9.0-beta.2
  • Loading branch information
pablormier authored Aug 16, 2021
2 parents 8d57bfb + 13b5356 commit 059bbde
Show file tree
Hide file tree
Showing 10 changed files with 118 additions and 60 deletions.
27 changes: 19 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,21 +1,30 @@
# Introduction
<div align="center">
<a href="https://metexplore.github.io/miom"><img align="center" src="https://github.com/MetExplore/miom/raw/development/docs/assets/img/miom_v1.png" alt="MIOM" width="545" title="MIOM: Mixed Integer Optimization for Metabolism"/>
</a>
</div>

<div align="center">

[![Try It Online](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/drive/1JAOEHLlRCW8GziIpBqkFwJL2ha3OEOWJ?usp=sharing)
[![PyPI version](https://badge.fury.io/py/miom.svg)](https://badge.fury.io/py/miom)
[![PyPI](https://img.shields.io/pypi/v/miom?style=flat-square&label=PyPI&logo=pypi&logoColor=white)](https://pypi.org/project/miom/)
![Tests](https://github.com/metexplore/miom/actions/workflows/unit-tests.yml/badge.svg)

</div>

---

https://metexplore.github.io/miom

__MIOM__ (Mixed Integer Optimization for Metabolism) is a python library for creating and solving complex optimization problems using genome-scale metabolic networks, in just a few lines.

MIOM offers a high-level API that leverages the power of modern Mixed Integer Optimization (MIO) solvers to easily define steady-state metabolic optimization problems, from simple Flux Balance Analysis (FBA) simulations, to more complex problems, such as sparse FBA or context-specific reconstruction problems, and solve them the __required level of optimality__.

MIOM uses the [PICOS](https://picos-api.gitlab.io/picos/) and the [Python-MIP](https://www.python-mip.com/) libraries to build and solve the optimization problems using many commercial, academic and free solvers. It is also compatible and complementary to [cobrapy](https://opencobra.github.io/cobrapy/).
MIOM offers a high-level API that leverages the power of modern Mixed Integer Optimization (MIO) solvers to easily define steady-state metabolic optimization problems, from simple Flux Balance Analysis (FBA) simulations, to more complex problems, such as sparse FBA or context-specific reconstruction algorithms, and solve them the __required level of optimality__. It supports many free and commercial solvers thanks to the integration with the [PICOS](https://picos-api.gitlab.io/picos/) and the [Python-MIP](https://www.python-mip.com/). It is also compatible and complementary to [cobrapy](https://opencobra.github.io/cobrapy/).

Here is an example of how to implement FBA and Sparse FBA to maximize flux through the biomass reaction in the Recon3D model with MIOM:

```python
import miom

# Download the Recon3D metabolic network and find the maximum flux value
# through the biomass_reaction
model = (miom
.load('@BiGG/Recon3D.miom')
.steady_state()
Expand All @@ -25,7 +34,9 @@ model = (miom
print("Optimal flux:", model.get_fluxes('biomass_reaction'))
print("Number of active reactions:", sum(abs(model.get_fluxes()) > 1e-8))

# Minimize the number of reactions preserving the optimal flux
# Transform the previous FBA optimization problem into a Sparse FBA problem (MIP).
# Solving this problem returns the minimum number of active reactions preserving
# the optimal flux through the biomass_reaction
V, X = (model
.setup(opt_tol=0.05)
.set_fluxes_for('biomass_reaction')
Expand Down Expand Up @@ -77,7 +88,7 @@ To make things even easier, the method `load_gem` can import any model from this

```python
human1 = miom.mio.load_gem("@SysBioChalmers/Human-GEM.miom")
recon3d = miom.mio.load_gem("@BiGG/Human-GEM.miom")
recon3d = miom.mio.load_gem("@BiGG/Recon3D.miom")
```

Here is an example of how to load a metabolic network and maximize the flux through a target reaction using FBA, and then how to modify the original problem to implement the sparse FBA problem adding only a few lines to the original problem:
Expand Down
Binary file added docs/assets/img/miom_v1.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/assets/img/miom_v1_fit.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
27 changes: 16 additions & 11 deletions docs/index.md
Original file line number Diff line number Diff line change
@@ -1,19 +1,24 @@
# Introduction
<a href="https://metexplore.github.io/miom"><img src="assets/img/miom_v1_fit.png" alt="MIOM" title="MIOM: Mixed Integer Optimization for Metabolism"/></a>

[![Try It Online](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/drive/1JAOEHLlRCW8GziIpBqkFwJL2ha3OEOWJ?usp=sharing)
[![PyPI version](https://badge.fury.io/py/miom.svg)](https://badge.fury.io/py/miom)
[![PyPI](https://img.shields.io/pypi/v/miom?style=flat-square&label=PyPI&logo=pypi&logoColor=white)](https://pypi.org/project/miom/)
![Tests](https://github.com/metexplore/miom/actions/workflows/unit-tests.yml/badge.svg)

# Introduction

__MIOM__ (Mixed Integer Optimization for Metabolism) is a python library for creating and solving complex optimization problems using genome-scale metabolic networks, in just a few lines.

MIOM offers a high-level API that leverages the power of modern Mixed Integer Optimization (MIO) solvers to easily define steady-state metabolic optimization problems, from simple Flux Balance Analysis (FBA) simulations, to more complex problems, such as sparse FBA or context-specific reconstruction problems, and solve them the __required level of optimality__.
MIOM offers a high-level API that leverages the power of modern Mixed Integer Optimization (MIO) solvers to easily define steady-state metabolic optimization problems, from simple Flux Balance Analysis (FBA) simulations, to more complex problems, such as [sparse FBA](https://metexplore.github.io/miom/examples/sparse_fba/) or context-specific reconstruction algorithms (see for example [iMAT](https://metexplore.github.io/miom/examples/imat/) or [Fastcore](https://metexplore.github.io/miom/examples/fastcore/)), and solve them the __required level of optimality__. It supports many free and commercial solvers thanks to the integration with the [PICOS](https://picos-api.gitlab.io/picos/) and the [Python-MIP](https://www.python-mip.com/). It is also compatible and complementary to [cobrapy](https://opencobra.github.io/cobrapy/).

MIOM uses the [PICOS](https://picos-api.gitlab.io/picos/) and the [Python-MIP](https://www.python-mip.com/) libraries to build and solve the optimization problems using many commercial, academic and free solvers. It is also compatible and complementary to [cobrapy](https://opencobra.github.io/cobrapy/).

Here is an example of how to implement FBA and Sparse FBA to maximize flux through the biomass reaction in the Recon3D model with MIOM:
!!! warning
This library is functional but still in a very early stage. API is still not stable and might be changed in future versions.

```python
import miom

# Download the Recon3D metabolic network and find the maximum flux value
# through the biomass_reaction
model = (miom
.load('@BiGG/Recon3D.miom')
.steady_state()
Expand All @@ -23,7 +28,9 @@ model = (miom
print("Optimal flux:", model.get_fluxes('biomass_reaction'))
print("Number of active reactions:", sum(abs(model.get_fluxes()) > 1e-8))

# Minimize the number of reactions preserving the optimal flux
# Transform the previous FBA optimization problem into a Sparse FBA problem (MIP).
# Solving this problem returns the minimum number of active reactions preserving
# the optimal flux through the biomass_reaction
V, X = (model
.setup(opt_tol=0.05)
.set_fluxes_for('biomass_reaction')
Expand All @@ -34,8 +41,6 @@ V, X = (model
print("Number of active reactions:", sum(abs(V) > 1e-8))
```

!!! warning
This library is functional but still in a very early stage. API is still not stable and might be changed in future versions.

## Installation

Expand Down Expand Up @@ -75,8 +80,8 @@ MIOM includes its own lightweight and portable format (.miom) for loading and st
To make things even easier, the method `load_gem` can import any model from this repository using the relative path, for example:

```python
human1 = miom.mio.load_gem("@SysBioChalmers/Human-GEM.miom")
recon3d = miom.mio.load_gem("@BiGG/Human-GEM.miom")
human1 = miom.mio.load_gem("@SysBioChalmers/Human-GEM/v1.9.0")
recon3d = miom.mio.load_gem("@BiGG/Recon3D.miom")
```

Here is an example of how to load a metabolic network and maximize the flux through a target reaction using FBA, and then how to modify the original problem to implement the sparse FBA problem adding only a few lines to the original problem:
Expand Down Expand Up @@ -169,7 +174,7 @@ model = (miom

## Documentation

The documentation of the library can be found at https://metexplore.github.io/miom/
The documentation of the library can be found at [https://metexplore.github.io/miom/](https://metexplore.github.io/miom/)

## How to cite

Expand Down
2 changes: 1 addition & 1 deletion miom/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,4 @@
)

__all__ = ["load", "Solvers", "Comparator", "ExtractionMode"]
__version__ = "0.9.0-beta.1"
__version__ = "0.9.0-beta.2"
35 changes: 29 additions & 6 deletions miom/__main__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@
import miom
import os

def convert_list_gems(file_or_list, folder=None):
for input_file in file_or_list:
def convert_list_gems(input_files, output=None):
for i, input_file in enumerate(input_files):
input_file = input_file.strip()
# Check if input_file is a valid file and it exists
if not (miom.mio._is_url(input_file) or (os.path.isfile(input_file) and os.path.getsize(input_file) > 0)):
Expand All @@ -17,8 +17,22 @@ def convert_list_gems(file_or_list, folder=None):
m = miom.mio.load_gem(input_file)
print(f"Loaded network with {m.num_reactions} reactions (in-memory size: {m.object_size:.2f} MB)")
# Concatenate folder and output file
if folder is not None:
output_file = os.path.join(folder, output_file)
if output is not None:
if isinstance(output, list) and len(output) == len(input_files):
output_file = output[i]
# Get absolute path of the folder of the file
abspath = os.path.dirname(os.path.abspath(output_file))
if not os.path.isdir(abspath):
# Try to create a folder
try:
print(f"Creating directory {abspath}...")
os.makedirs(abspath)
except OSError as e:
print(f"Cannot create {abspath} folder: {e}")
elif os.path.isdir(output):
output_file = os.path.join(output, output_file)
else:
raise ValueError("The provided output is not a folder or a list of destinations for each file")
print(f"Exporting to {output_file}...")
miom.mio.export_gem(m, output_file)
# Calculate the MBs of the output_file and print it
Expand All @@ -33,12 +47,21 @@ def convert_list_gems(file_or_list, folder=None):
def convert_gem(args):
print(f"MIOM v{miom.__version__}")
input = args.input
output = args.output
if isinstance(input, str):
if os.path.isfile(input):
# Open file and get all the lines into a list
print(f"Reading list of files from {input}...")
with open(input, "r") as f:
input = f.readlines()
in_files, out_files = [], []
input = f.readlines()
for line in input:
if ";" in line:
in_path, out_path = line.split(";")
in_files.append(in_path.strip())
out_files.append(out_path.strip())
input = in_files
output = out_files
elif miom.mio._is_url(input):
print("Imporing file from URL")
input = [input]
Expand All @@ -50,7 +73,7 @@ def convert_gem(args):
if not os.path.isdir(input_folder):
raise FileNotFoundError(f"{input_folder} is not a valid folder, a file or an URL")
input = [os.path.join(input, f) for f in os.listdir(input)]
convert_list_gems(input, args.output)
convert_list_gems(input, output)


def get_args():
Expand Down
39 changes: 30 additions & 9 deletions miom/mio.py
Original file line number Diff line number Diff line change
Expand Up @@ -133,10 +133,16 @@ def load_gem(model_or_path):
matrix, the list of reactions with the lower and upper bounds, the associated
genes and GPR rules, and the list of metabolites.
"""
extensions = ['.xml', '.yml', '.json', '.mat', '.miom']
if isinstance(model_or_path, str):
file = model_or_path
if model_or_path.startswith("@"):
file = _download(DEFAULT_REPOSITORY + model_or_path[1:])
# Check if the file has a valid file extension
if not any(file.endswith(ext) for ext in extensions):
# Assume is a relative url pointing to a version
file = _download(DEFAULT_REPOSITORY + model_or_path[1:] + "/default.miom")
else:
file = _download(DEFAULT_REPOSITORY + model_or_path[1:])
elif _is_url(model_or_path):
file = _download(model_or_path)
ext = pathlib.Path(file).suffix
Expand Down Expand Up @@ -192,15 +198,30 @@ def _cobra_to_miom(model):
raise ImportError("Cobrapy package is not installed, "
"but required to read and import metabolic networks", e)
S = create_stoichiometric_matrix(model)
rxn_data = [(rxn.id, rxn.lower_bound, rxn.upper_bound, rxn.subsystem, rxn.gene_reaction_rule)
for rxn in model.reactions]
subsystems = []
for rxn in model.reactions:
subsys = rxn.subsystem
list_subsystem_rxn = []
# For .mat models, the subsystem can be loaded as a string repr of a numpy array
if isinstance(subsys, str) and (subsys.startswith("array(") or subsys.startswith("[array(")):
from numpy import array
subsys = eval(subsys)
# A list containing a numpy array?
for s in subsys:
if "tolist" in dir(s):
list_subsystem_rxn.extend(s.tolist())
else:
list_subsystem_rxn.append(s)
if len(list_subsystem_rxn) == 1:
list_subsystem_rxn = list_subsystem_rxn[0]
subsystems.append(list_subsystem_rxn)
elif "tolist" in dir(rxn.subsystem):
subsystems.append(rxn.subsystem.tolist())
else:
subsystems.append(rxn.subsystem)
rxn_data = [(rxn.id, rxn.lower_bound, rxn.upper_bound, subsystem, rxn.gene_reaction_rule)
for rxn, subsystem in zip(model.reactions, subsystems)]
met_data = [(met.id, met.name, met.formula) for met in model.metabolites]
#id_max_length = max(len(rxn.id) for rxn in model.reactions)
#subsyst_max_length = max((len(rxn.subsystem)) for rxn in model.reactions)
#metid_max_length = max((len(met.id)) for met in model.metabolites)
#metname_max_length = max((len(met.name)) for met in model.metabolites)
#metform_max_length = max((len(met.formula) if met.formula is not None else 0) for met in model.metabolites)
#gpr_max_length = max(len(rxn.gene_reaction_rule) for rxn in model.reactions)
R = np.array(rxn_data, dtype=[
('id', 'object'),
('lb', 'float'),
Expand Down
Loading

0 comments on commit 059bbde

Please sign in to comment.