Source code for BFAIR.mfa.sampling.constraints

import time
import functools


def timer(func):
    """
    Wrapper that reports how long it took to execute a function.
    """

    @functools.wraps(func)
    def wrapper(*args, **kwargs):
        print("--- start ---")
        start_time = time.time()
        output = func(*args, **kwargs)
        elapsed_time = time.time() - start_time
        min_, sec = divmod(round(elapsed_time), 60)
        hour, min_ = divmod(min_, 60)
        print(f"{func.__name__} takes {hour}h: {min_}min: {sec}sec to run")
        print("--- end ---")
        return output

    return wrapper


def _reshape_fluxes(fittedFluxes):
    """
    Reshaped the flux information from a dataframe to a dict to be used in
    constraint assigning functions.
    """
    MFA_flux_bounds = {}
    for i, rxn in fittedFluxes.iterrows():
        MFA_flux_bounds[rxn["rxn_id"]] = [rxn["flux_ub"], rxn["flux_lb"]]
    return MFA_flux_bounds


def _adjust_bounds(model, rxn, bounds):
    """
    Applied new bounds to specified reactions in a cobra model.
    """
    skip = False
    if bounds[0] < bounds[1]:  # to fix the issue with negaive values above
        try:
            model.reactions.get_by_id(rxn).lower_bound = round(bounds[0], 1)
            model.reactions.get_by_id(rxn).upper_bound = round(bounds[1], 1)
        except KeyError:
            print(f"Did not work for {rxn}")
            skip = True
    else:
        try:
            model.reactions.get_by_id(rxn).upper_bound = round(bounds[0], 1)
            model.reactions.get_by_id(rxn).lower_bound = round(bounds[1], 1)
        except KeyError:
            print(f"Did not work for {rxn}")
            skip = True
    return model, skip


def _restart_message(restart_counter):
    """
    Prints a message if `add_feasible_constraints()` had to restart.
    """
    extra_minus = "-" * len(str(restart_counter))
    print(f"--------------------------{extra_minus}")
    print(f"Total number of restarts: {restart_counter}")


[docs]@timer def add_constraints(model_input, fittedFluxes): """ Adds all the constraints defined in the input to a metabolic model. Parameters ---------- model_input : cobra.Model Metabolic model. fittedFluxes : pandas.DataFrame Dataframe (reimported output of an INCA simulation) that contains the confidence intervals predicted for the model. Returns ------- model : cobra.Model Metabolic model with adjusted constraints. """ MFA_fluxes = _reshape_fluxes(fittedFluxes) model = model_input.copy() for rxn, bounds in MFA_fluxes.items(): model, skip = _adjust_bounds(model, rxn, bounds) return model
[docs]@timer def add_feasible_constraints(model_input, fittedFluxes, min_val=0): """ Adds contraints to a metabolic model one by one; always checking that is still produces a feasible solution and that the predicted objective does not fall below a predefined value. If that were to happen, the function would restart but skip the reaction that caused the issue in the next iteration. Parameters ---------- model_input : cobra.Model Metabolic model. fittedFluxes : pandas.DataFrame Dataframe (reimported output of an INCA simulation) that contains the confidence intervals predicted for the model. min_val : float Minimum allowed value for the optimized solution predicted for the objective. Preset to `0`. Returns ------- model : cobra.Model Feasible metabolic model with added constraints. problems : list list of the reaction names of the reactions whose added constraints caused the model to fail the tests (feasibilty or minimum value). """ no_restart = False problems = [] restart_counter = 0 MFA_fluxes = _reshape_fluxes(fittedFluxes) while no_restart is False: model = model_input.copy() for rxn, bounds in MFA_fluxes.items(): if rxn in problems: continue model, skip = _adjust_bounds(model, rxn, bounds) if skip: continue solution_after_adj = model.optimize() if ( solution_after_adj.objective_value is not None and solution_after_adj.objective_value >= min_val ): no_restart = True else: print(f"Solution infeasible if adding {rxn}") problems.append(rxn) no_restart = False restart_counter += 1 break _restart_message(restart_counter) return model, problems