From 675f3adee51d3022402ddb00ca7a652d98d66b57 Mon Sep 17 00:00:00 2001 From: boilpy Date: Tue, 20 Sep 2022 18:49:59 -0500 Subject: [PATCH 1/3] feat: add progress to fva --- src/cobra/flux_analysis/variability.py | 72 ++++++++++++++++++-------- 1 file changed, 51 insertions(+), 21 deletions(-) diff --git a/src/cobra/flux_analysis/variability.py b/src/cobra/flux_analysis/variability.py index 4b1066f2b..f2054e56f 100644 --- a/src/cobra/flux_analysis/variability.py +++ b/src/cobra/flux_analysis/variability.py @@ -1,6 +1,5 @@ """Provide variability based methods such as flux variability or gene essentiality.""" - import logging from typing import TYPE_CHECKING, List, Optional, Set, Tuple, Union from warnings import warn @@ -8,6 +7,7 @@ import numpy as np import pandas as pd from optlang.symbolics import Zero +from rich.progress import Progress from ..core import Configuration, get_solution from ..util import ProcessPool @@ -24,7 +24,28 @@ logger = logging.getLogger(__name__) configuration = Configuration() - + +class _FakeProgress: + def __enter__(self): + return self + + def __exit__(self, *args): + pass + + def add_task(self, *args, **kwargs): + return object() + + def update(*args, **kwargs): + pass + +def _update_progress_bar( + progress_bar, + task, + advance: int, +) -> None: + """Update progress bar. + """ + progress_bar.update(task, advance=advance) def _init_worker(model: "Model", loopless: bool, sense: str) -> None: """Initialize a global model object for multiprocessing. @@ -88,7 +109,6 @@ def _fva_step(reaction_id: str) -> Tuple[str, float]: ) return reaction_id, value - def flux_variability_analysis( model: "Model", reaction_list: Optional[List[Union["Reaction", str]]] = None, @@ -230,25 +250,35 @@ def flux_variability_analysis( model.add_cons_vars([flux_sum, flux_sum_constraint]) model.objective = Zero # This will trigger the reset as well - for what in ("minimum", "maximum"): - if processes > 1: - # We create and destroy a new pool here in order to set the - # objective direction for all reactions. This creates a - # slight overhead but seems the most clean. - chunk_size = len(reaction_ids) // processes - with ProcessPool( - processes, - initializer=_init_worker, - initargs=(model, loopless, what[:3]), - ) as pool: - for rxn_id, value in pool.imap_unordered( - _fva_step, reaction_ids, chunksize=chunk_size - ): + + _Progress = Progress + if logger.level != logging.INFO: + _Progress = _FakeProgress + + with _Progress() as progress: + progress_task = progress.add_task("[cyan]Performing FVA...", total=num_reactions) + + for what in ("minimum", "maximum"): + if processes > 1: + # We create and destroy a new pool here in order to set the + # objective direction for all reactions. This creates a + # slight overhead but seems the most clean. + chunk_size = len(reaction_ids) // processes + with ProcessPool( + processes, + initializer=_init_worker, + initargs=(model, loopless, what[:3]), + ) as pool: + for i, (rxn_id, value) in enumerate(pool.imap_unordered( + _fva_step, reaction_ids, chunksize=chunk_size + )): + fva_result.at[rxn_id, what] = value + _update_progress_bar(progress, progress_task, (i+1)/num_reactions) + else: + _init_worker(model, loopless, what[:3]) + for i, (rxn_id, value) in enumerate(map(_fva_step, reaction_ids)): fva_result.at[rxn_id, what] = value - else: - _init_worker(model, loopless, what[:3]) - for rxn_id, value in map(_fva_step, reaction_ids): - fva_result.at[rxn_id, what] = value + _update_progress_bar(progress, progress_task, (i+1)/num_reactions) return fva_result[["minimum", "maximum"]] From 2aebab4ee7da3d0dd60bc05261a29ab914164d3e Mon Sep 17 00:00:00 2001 From: boilpy Date: Tue, 20 Sep 2022 18:51:07 -0500 Subject: [PATCH 2/3] docs: updated docs --- release-notes/next-release.md | 1 + 1 file changed, 1 insertion(+) diff --git a/release-notes/next-release.md b/release-notes/next-release.md index 8bb9bbe10..0d21e7f12 100644 --- a/release-notes/next-release.md +++ b/release-notes/next-release.md @@ -3,6 +3,7 @@ ## New features * View number of genes in model notebook representation. +* Add progress bar to `flux_variability_analysis`. ## Fixes From eb62c8a29ecc6fceb652746c5ca3ebcbad9e6a03 Mon Sep 17 00:00:00 2001 From: boilpy Date: Tue, 20 Sep 2022 19:04:27 -0500 Subject: [PATCH 3/3] lint: fixed black --- src/cobra/flux_analysis/variability.py | 31 +++++++++++++++++--------- 1 file changed, 21 insertions(+), 10 deletions(-) diff --git a/src/cobra/flux_analysis/variability.py b/src/cobra/flux_analysis/variability.py index f2054e56f..1fa2bea12 100644 --- a/src/cobra/flux_analysis/variability.py +++ b/src/cobra/flux_analysis/variability.py @@ -24,7 +24,8 @@ logger = logging.getLogger(__name__) configuration = Configuration() - + + class _FakeProgress: def __enter__(self): return self @@ -38,15 +39,16 @@ def add_task(self, *args, **kwargs): def update(*args, **kwargs): pass + def _update_progress_bar( progress_bar, task, advance: int, ) -> None: - """Update progress bar. - """ + """Update progress bar.""" progress_bar.update(task, advance=advance) + def _init_worker(model: "Model", loopless: bool, sense: str) -> None: """Initialize a global model object for multiprocessing. @@ -109,6 +111,7 @@ def _fva_step(reaction_id: str) -> Tuple[str, float]: ) return reaction_id, value + def flux_variability_analysis( model: "Model", reaction_list: Optional[List[Union["Reaction", str]]] = None, @@ -256,8 +259,10 @@ def flux_variability_analysis( _Progress = _FakeProgress with _Progress() as progress: - progress_task = progress.add_task("[cyan]Performing FVA...", total=num_reactions) - + progress_task = progress.add_task( + "[cyan]Performing FVA...", total=num_reactions + ) + for what in ("minimum", "maximum"): if processes > 1: # We create and destroy a new pool here in order to set the @@ -269,16 +274,22 @@ def flux_variability_analysis( initializer=_init_worker, initargs=(model, loopless, what[:3]), ) as pool: - for i, (rxn_id, value) in enumerate(pool.imap_unordered( - _fva_step, reaction_ids, chunksize=chunk_size - )): + for i, (rxn_id, value) in enumerate( + pool.imap_unordered( + _fva_step, reaction_ids, chunksize=chunk_size + ) + ): fva_result.at[rxn_id, what] = value - _update_progress_bar(progress, progress_task, (i+1)/num_reactions) + _update_progress_bar( + progress, progress_task, (i + 1) / num_reactions + ) else: _init_worker(model, loopless, what[:3]) for i, (rxn_id, value) in enumerate(map(_fva_step, reaction_ids)): fva_result.at[rxn_id, what] = value - _update_progress_bar(progress, progress_task, (i+1)/num_reactions) + _update_progress_bar( + progress, progress_task, (i + 1) / num_reactions + ) return fva_result[["minimum", "maximum"]]