def _fva_step(reaction_id): global _model global _loopless rxn = _model.reactions.get_by_id(reaction_id) # The previous objective assignment already triggers a reset # so directly update coefs here to not trigger redundant resets # in the history manager which can take longer than the actual # FVA for small models _model.solver.objective.set_linear_coefficients({ rxn.forward_variable: 1, rxn.reverse_variable: -1 }) _model.slim_optimize() sutil.check_solver_status(_model.solver.status) if _loopless: value = loopless_fva_iter(_model, rxn) else: value = _model.solver.objective.value # handle infeasible case if value is None: value = float("nan") LOGGER.warning( "Could not get flux for reaction %s, setting " "it to NaN. This is usually due to numerical instability.", rxn.id, ) _model.solver.objective.set_linear_coefficients({ rxn.forward_variable: 0, rxn.reverse_variable: 0 }) return reaction_id, value
def optimize(self, objective_sense=None, raise_error=False, **kwargs): """ Optimize the model using flux balance analysis. Parameters ---------- objective_sense : {None, 'maximize' 'minimize'}, optional Whether fluxes should be maximized or minimized. In case of None, the previous direction is used. raise_error : bool If true, raise an OptimizationError if solver status is not optimal. solver : {None, 'glpk', 'cglpk', 'gurobi', 'cplex'}, optional If unspecified will use the currently defined `self.solver` otherwise it will use the given solver and update the attribute. quadratic_component : {None, scipy.sparse.dok_matrix}, optional The dimensions should be (n, n) where n is the number of reactions. This sets the quadratic component (Q) of the objective coefficient, adding :math:`\\frac{1}{2} v^T \cdot Q \cdot v` to the objective. Ignored for optlang based solvers. tolerance_feasibility : float Solver tolerance for feasibility. Ignored for optlang based solvers tolerance_markowitz : float Solver threshold during pivot. Ignored for optlang based solvers time_limit : float Maximum solver time (in seconds). Ignored for optlang based solvers Notes ----- Only the most commonly used parameters are presented here. Additional parameters for cobra.solvers may be available and specified with the appropriate keyword argument. """ legacy, solver = choose_solver(self, solver=kwargs.get("solver")) original_direction = self.objective.direction if legacy: if objective_sense is None: objective_sense = { "max": "maximize", "min": "minimize" }[original_direction] solution = optimize(self, objective_sense=objective_sense, **kwargs) check_solver_status(solution.status, raise_error=raise_error) return solution self.solver = solver self.objective.direction = \ {"maximize": "max", "minimize": "min"}.get( objective_sense, original_direction) self.slim_optimize() solution = get_solution(self, raise_error=raise_error) self.objective.direction = original_direction return solution
def flux(self): """ The flux value in the most recent solution. Flux is the primal value of the corresponding variable in the model. Warnings -------- * Accessing reaction fluxes through a `Solution` object is the safer, preferred, and only guaranteed to be correct way. You can see how to do so easily in the examples. * Reaction flux is retrieved from the currently defined `self._model.solver`. The solver status is checked but there are no guarantees that the current solver state is the one you are looking for. * If you modify the underlying model after an optimization, you will retrieve the old optimization values. Raises ------ RuntimeError If the underlying model was never optimized beforehand or the reaction is not part of a model. OptimizationError If the solver status is anything other than 'optimal'. AssertionError If the flux value is not within the bounds. Examples -------- >>> import cobra.test >>> model = cobra.test.create_test_model("textbook") >>> solution = model.optimize() >>> model.reactions.PFK.flux 7.477381962160283 >>> solution.fluxes.PFK 7.4773819621602833 """ try: check_solver_status(self._model.solver.status) return self.forward_variable.primal - self.reverse_variable.primal except AttributeError: raise RuntimeError("reaction '{}' is not part of a model".format( self.id)) # Due to below all-catch, which sucks, need to reraise these. except (RuntimeError, OptimizationError) as err: raise_with_traceback(err) # Would love to catch CplexSolverError and GurobiError here. except Exception as err: raise_from( OptimizationError( "Likely no solution exists. Original solver message: {}." "".format(str(err))), err, )
def shadow_price(self): """ The shadow price in the most recent solution. Shadow price is the dual value of the corresponding constraint in the model. Warnings -------- * Accessing shadow prices through a `Solution` object is the safer, preferred, and only guaranteed to be correct way. You can see how to do so easily in the examples. * Shadow price is retrieved from the currently defined `self._model.solver`. The solver status is checked but there are no guarantees that the current solver state is the one you are looking for. * If you modify the underlying model after an optimization, you will retrieve the old optimization values. Raises ------ RuntimeError If the underlying model was never optimized beforehand or the metabolite is not part of a model. OptimizationError If the solver status is anything other than 'optimal'. Examples -------- >>> import cobra >>> import cobra.test >>> model = cobra.test.create_test_model("textbook") >>> solution = model.optimize() >>> model.metabolites.glc__D_e.shadow_price -0.09166474637510488 >>> solution.shadow_prices.glc__D_e -0.091664746375104883 """ try: check_solver_status(self._model.solver.status) return self._model.constraints[self.id].dual except AttributeError: raise RuntimeError("metabolite '{}' is not part of a model".format( self.id)) # Due to below all-catch, which sucks, need to reraise these. except (RuntimeError, OptimizationError) as err: raise_with_traceback(err) # Would love to catch CplexSolverError and GurobiError here. except Exception as err: raise_from( OptimizationError( "Likely no solution exists. Original solver message: {}." "".format(str(err))), err, )
def _fva_optlang(model, reaction_list, fraction, loopless): """Helper function to perform FVA with the optlang interface. Parameters ---------- model : a cobra model reaction_list : list of reactions Returns ------- dict A dictionary containing the results. """ fva_results = {str(rxn): {} for rxn in reaction_list} prob = model.problem with model as m: m.solver.optimize() if m.solver.status != "optimal": raise ValueError("There is no optimal solution " "for the chosen objective!") # Add objective as a variable to the model than set to zero # This also uses the fraction to create the lower bound for the # old objective fva_old_objective = prob.Variable( "fva_old_objective", lb=fraction * m.solver.objective.value) fva_old_obj_constraint = prob.Constraint( m.solver.objective.expression - fva_old_objective, lb=0, ub=0, name="fva_old_objective_constraint") m.add_cons_vars([fva_old_objective, fva_old_obj_constraint]) model.objective = S.Zero # This will trigger the reset as well for what in ("minimum", "maximum"): sense = "min" if what == "minimum" else "max" for rxn in reaction_list: r_id = str(rxn) rxn = m.reactions.get_by_id(r_id) # The previous objective assignment already triggers a reset # so directly update coefs here to not trigger redundant resets # in the history manager which can take longer than the actual # FVA for small models m.solver.objective.set_linear_coefficients( {rxn.forward_variable: 1, rxn.reverse_variable: -1}) m.solver.objective.direction = sense m.solver.optimize() sutil.check_solver_status(m.solver.status) if loopless: value = loopless_fva_iter(m, rxn) else: value = m.solver.objective.value fva_results[r_id][what] = value m.solver.objective.set_linear_coefficients( {rxn.forward_variable: 0, rxn.reverse_variable: 0}) return fva_results
def get_solution(model, reactions=None, metabolites=None): """ Generate a solution representation of the current solver state. Parameters --------- model : cobra.Model The model whose reactions to retrieve values for. reactions : list, optional An iterable of `cobra.Reaction` objects. Uses `model.reactions` by default. metabolites : list, optional An iterable of `cobra.Metabolite` objects. Uses `model.metabolites` by default. Returns ------- cobra.Solution Note ---- This is only intended for the `optlang` solver interfaces and not the legacy solvers. """ check_solver_status(model.solver.status) if reactions is None: reactions = model.reactions if metabolites is None: metabolites = model.metabolites rxn_index = [rxn.id for rxn in reactions] fluxes = zeros(len(reactions)) var_primals = model.solver.primal_values reduced = zeros(len(reactions)) var_duals = model.solver.reduced_costs # reduced costs are not always defined, e.g. for integer problems if var_duals[rxn_index[0]] is None: reduced.fill(nan) for (i, rxn) in enumerate(reactions): fluxes[i] = var_primals[rxn.id] - var_primals[rxn.reverse_id] else: for (i, rxn) in enumerate(reactions): forward = rxn.id reverse = rxn.reverse_id fluxes[i] = var_primals[forward] - var_primals[reverse] reduced[i] = var_duals[forward] - var_duals[reverse] met_index = [met.id for met in metabolites] constr_duals = model.solver.shadow_prices shadow = asarray([constr_duals[met.id] for met in metabolites]) return Solution(model.solver.objective.value, model.solver.status, reactions, Series(index=rxn_index, data=fluxes), Series(index=rxn_index, data=reduced), metabolites, Series(index=met_index, data=shadow))
def shadow_price(self): """ The shadow price in the most recent solution. Shadow price is the dual value of the corresponding constraint in the model. Warnings -------- * Accessing shadow prices through a `Solution` object is the safer, preferred, and only guaranteed to be correct way. You can see how to do so easily in the examples. * Shadow price is retrieved from the currently defined `self._model.solver`. The solver status is checked but there are no guarantees that the current solver state is the one you are looking for. * If you modify the underlying model after an optimization, you will retrieve the old optimization values. Raises ------ RuntimeError If the underlying model was never optimized beforehand or the metabolite is not part of a model. OptimizationError If the solver status is anything other than 'optimal'. Examples -------- >>> import cobra >>> import cobra.test >>> model = cobra.test.create_test_model("textbook") >>> solution = model.optimize() >>> model.metabolites.glc__D_e.shadow_price -0.09166474637510488 >>> solution.shadow_prices.glc__D_e -0.091664746375104883 """ try: check_solver_status(self._model.solver.status) return self._model.constraints[self.id].dual except AttributeError: raise RuntimeError( "metabolite '{}' is not part of a model".format(self.id)) # Due to below all-catch, which sucks, need to reraise these. except (RuntimeError, OptimizationError) as err: raise_with_traceback(err) # Would love to catch CplexSolverError and GurobiError here. except Exception as err: raise_from(OptimizationError( "Likely no solution exists. Original solver message: {}." "".format(str(err))), err)
def flux(self): """ The flux value in the most recent solution. Flux is the primal value of the corresponding variable in the model. Warnings -------- * Accessing reaction fluxes through a `Solution` object is the safer, preferred, and only guaranteed to be correct way. You can see how to do so easily in the examples. * Reaction flux is retrieved from the currently defined `self._model.solver`. The solver status is checked but there are no guarantees that the current solver state is the one you are looking for. * If you modify the underlying model after an optimization, you will retrieve the old optimization values. Raises ------ RuntimeError If the underlying model was never optimized beforehand or the reaction is not part of a model. OptimizationError If the solver status is anything other than 'optimal'. AssertionError If the flux value is not within the bounds. Examples -------- >>> import cobra.test >>> model = cobra.test.create_test_model("textbook") >>> solution = model.optimize() >>> model.reactions.PFK.flux 7.477381962160283 >>> solution.fluxes.PFK 7.4773819621602833 """ try: check_solver_status(self._model.solver.status) return self.forward_variable.primal - self.reverse_variable.primal except AttributeError: raise RuntimeError( "reaction '{}' is not part of a model".format(self.id)) # Due to below all-catch, which sucks, need to reraise these. except (RuntimeError, OptimizationError) as err: raise_with_traceback(err) # Would love to catch CplexSolverError and GurobiError here. except Exception as err: raise_from(OptimizationError( "Likely no solution exists. Original solver message: {}." "".format(str(err))), err)
def flux(self) -> float: """Get flux value in the most recent solution. Flux is the primal value of the corresponding variable in the model. Warnings -------- * Accessing reaction fluxes through a `Solution` object is the safer, preferred, and only guaranteed to be correct way. You can see how to do so easily in the examples. * Reaction flux is retrieved from the currently defined `self._model.solver`. The solver status is checked but there are no guarantees that the current solver state is the one you are looking for. * If you modify the underlying model after an optimization, you will retrieve the old optimization values. Raises ------ RuntimeError If the underlying model was never optimized beforehand or the reaction is not part of a model. OptimizationError If the solver status is anything other than 'optimal'. AssertionError If the flux value is not within the bounds. """ try: check_solver_status(self._model.solver.status) return self.forward_variable.primal - self.reverse_variable.primal except AttributeError: raise RuntimeError(f"Protein '{self.id}' is not part of a model.") # Due to below all-catch, which sucks, need to reraise these. except (RuntimeError, OptimizationError) as err: raise OptimizationError(err) # Would love to catch CplexSolverError and GurobiError here. except Exception as err: raise OptimizationError( f"Likely no solution exists. Original solver message: {err}." )
def _fva_step(reaction_id): global _model global _loopless rxn = _model.reactions.get_by_id(reaction_id) # The previous objective assignment already triggers a reset # so directly update coefs here to not trigger redundant resets # in the history manager which can take longer than the actual # FVA for small models _model.solver.objective.set_linear_coefficients({ rxn.forward_variable: 1, rxn.reverse_variable: -1 }) _model.slim_optimize() sutil.check_solver_status(_model.solver.status) if _loopless: value = loopless_fva_iter(_model, rxn) else: value = _model.solver.objective.value _model.solver.objective.set_linear_coefficients({ rxn.forward_variable: 0, rxn.reverse_variable: 0 }) return reaction_id, value
def flux_variability_analysis(model, reaction_list=None, loopless=False, fraction_of_optimum=1.0, pfba_factor=None): """ Determine the minimum and maximum possible flux value for each reaction. Parameters ---------- model : cobra.Model The model for which to run the analysis. It will *not* be modified. reaction_list : list of cobra.Reaction or str, optional The reactions for which to obtain min/max fluxes. If None will use all reactions in the model (default). loopless : boolean, optional Whether to return only loopless solutions. This is significantly slower. Please also refer to the notes. fraction_of_optimum : float, optional Must be <= 1.0. Requires that the objective value is at least the fraction times maximum objective value. A value of 0.85 for instance means that the objective has to be at least at 85% percent of its maximum. pfba_factor : float, optional Add an additional constraint to the model that requires the total sum of absolute fluxes must not be larger than this value times the smallest possible sum of absolute fluxes, i.e., by setting the value to 1.1 the total sum of absolute fluxes must not be more than 10% larger than the pFBA solution. Since the pFBA solution is the one that optimally minimizes the total flux sum, the ``pfba_factor`` should, if set, be larger than one. Setting this value may lead to more realistic predictions of the effective flux bounds. Returns ------- pandas.DataFrame A data frame with reaction identifiers as the index and two columns: - maximum: indicating the highest possible flux - minimum: indicating the lowest possible flux Notes ----- This implements the fast version as described in [1]_. Please note that the flux distribution containing all minimal/maximal fluxes does not have to be a feasible solution for the model. Fluxes are minimized/maximized individually and a single minimal flux might require all others to be suboptimal. Using the loopless option will lead to a significant increase in computation time (about a factor of 100 for large models). However, the algorithm used here (see [2]_) is still more than 1000x faster than the "naive" version using ``add_loopless(model)``. Also note that if you have included constraints that force a loop (for instance by setting all fluxes in a loop to be non-zero) this loop will be included in the solution. References ---------- .. [1] Computationally efficient flux variability analysis. Gudmundsson S, Thiele I. BMC Bioinformatics. 2010 Sep 29;11:489. doi: 10.1186/1471-2105-11-489, PMID: 20920235 .. [2] CycleFreeFlux: efficient removal of thermodynamically infeasible loops from flux distributions. Desouki AA, Jarre F, Gelius-Dietrich G, Lercher MJ. Bioinformatics. 2015 Jul 1;31(13):2159-65. doi: 10.1093/bioinformatics/btv096. """ if reaction_list is None: reaction_list = model.reactions else: reaction_list = model.reactions.get_by_any(reaction_list) prob = model.problem fva_results = DataFrame( { "minimum": zeros(len(reaction_list), dtype=float), "maximum": zeros(len(reaction_list), dtype=float) }, index=[r.id for r in reaction_list]) with model: # Safety check before setting up FVA. model.slim_optimize(error_value=None, message="There is no optimal solution for the " "chosen objective!") # Add the previous objective as a variable to the model then set it to # zero. This also uses the fraction to create the lower/upper bound for # the old objective. if model.solver.objective.direction == "max": fva_old_objective = prob.Variable("fva_old_objective", lb=fraction_of_optimum * model.solver.objective.value) else: fva_old_objective = prob.Variable("fva_old_objective", ub=fraction_of_optimum * model.solver.objective.value) fva_old_obj_constraint = prob.Constraint( model.solver.objective.expression - fva_old_objective, lb=0, ub=0, name="fva_old_objective_constraint") model.add_cons_vars([fva_old_objective, fva_old_obj_constraint]) if pfba_factor is not None: if pfba_factor < 1.: warn("The 'pfba_factor' should be larger or equal to 1.", UserWarning) with model: add_pfba(model, fraction_of_optimum=0) ub = model.slim_optimize(error_value=None) flux_sum = prob.Variable("flux_sum", ub=pfba_factor * ub) flux_sum_constraint = prob.Constraint( model.solver.objective.expression - flux_sum, lb=0, ub=0, name="flux_sum_constraint") model.add_cons_vars([flux_sum, flux_sum_constraint]) model.objective = Zero # This will trigger the reset as well for what in ("minimum", "maximum"): sense = "min" if what == "minimum" else "max" model.solver.objective.direction = sense for rxn in reaction_list: # The previous objective assignment already triggers a reset # so directly update coefs here to not trigger redundant resets # in the history manager which can take longer than the actual # FVA for small models model.solver.objective.set_linear_coefficients({ rxn.forward_variable: 1, rxn.reverse_variable: -1 }) model.slim_optimize() sutil.check_solver_status(model.solver.status) if loopless: value = loopless_fva_iter(model, rxn) else: value = model.solver.objective.value fva_results.at[rxn.id, what] = value model.solver.objective.set_linear_coefficients({ rxn.forward_variable: 0, rxn.reverse_variable: 0 }) return fva_results[["minimum", "maximum"]]
def get_solution(model, reactions=None, metabolites=None, raise_error=False): """ Generate a solution representation of the current solver state. Parameters --------- model : cobra.Model The model whose reactions to retrieve values for. reactions : list, optional An iterable of `cobra.Reaction` objects. Uses `model.reactions` by default. metabolites : list, optional An iterable of `cobra.Metabolite` objects. Uses `model.metabolites` by default. raise_error : bool If true, raise an OptimizationError if solver status is not optimal. Returns ------- cobra.Solution Note ---- This is only intended for the `optlang` solver interfaces and not the legacy solvers. """ check_solver_status(model.solver.status, raise_error=raise_error) if reactions is None: reactions = model.reactions if metabolites is None: metabolites = model.metabolites rxn_index = list() fluxes = empty(len(reactions)) reduced = empty(len(reactions)) var_primals = model.solver.primal_values shadow = empty(len(metabolites)) if model.solver.is_integer: reduced.fill(nan) shadow.fill(nan) for (i, rxn) in enumerate(reactions): rxn_index.append(rxn.id) fluxes[i] = var_primals[rxn.id] - var_primals[rxn.reverse_id] met_index = [met.id for met in metabolites] else: var_duals = model.solver.reduced_costs for (i, rxn) in enumerate(reactions): forward = rxn.id reverse = rxn.reverse_id rxn_index.append(forward) fluxes[i] = var_primals[forward] - var_primals[reverse] reduced[i] = var_duals[forward] - var_duals[reverse] met_index = list() constr_duals = model.solver.shadow_prices for (i, met) in enumerate(metabolites): met_index.append(met.id) shadow[i] = constr_duals[met.id] return Solution( model.solver.objective.value, model.solver.status, Series(index=rxn_index, data=fluxes, name="fluxes"), Series(index=rxn_index, data=reduced, name="reduced_costs"), Series(index=met_index, data=shadow, name="shadow_prices"))
def flux_variability_analysis(model, reaction_list=None, loopless=False, fraction_of_optimum=1.0, pfba_factor=None): """ Determine the minimum and maximum possible flux value for each reaction. Parameters ---------- model : cobra.Model The model for which to run the analysis. It will *not* be modified. reaction_list : list of cobra.Reaction or str, optional The reactions for which to obtain min/max fluxes. If None will use all reactions in the model (default). loopless : boolean, optional Whether to return only loopless solutions. This is significantly slower. Please also refer to the notes. fraction_of_optimum : float, optional Must be <= 1.0. Requires that the objective value is at least the fraction times maximum objective value. A value of 0.85 for instance means that the objective has to be at least at 85% percent of its maximum. pfba_factor : float, optional Add an additional constraint to the model that requires the total sum of absolute fluxes must not be larger than this value times the smallest possible sum of absolute fluxes, i.e., by setting the value to 1.1 the total sum of absolute fluxes must not be more than 10% larger than the pFBA solution. Since the pFBA solution is the one that optimally minimizes the total flux sum, the ``pfba_factor`` should, if set, be larger than one. Setting this value may lead to more realistic predictions of the effective flux bounds. Returns ------- pandas.DataFrame A data frame with reaction identifiers as the index and two columns: - maximum: indicating the highest possible flux - minimum: indicating the lowest possible flux Notes ----- This implements the fast version as described in [1]_. Please note that the flux distribution containing all minimal/maximal fluxes does not have to be a feasible solution for the model. Fluxes are minimized/maximized individually and a single minimal flux might require all others to be suboptimal. Using the loopless option will lead to a significant increase in computation time (about a factor of 100 for large models). However, the algorithm used here (see [2]_) is still more than 1000x faster than the "naive" version using ``add_loopless(model)``. Also note that if you have included constraints that force a loop (for instance by setting all fluxes in a loop to be non-zero) this loop will be included in the solution. References ---------- .. [1] Computationally efficient flux variability analysis. Gudmundsson S, Thiele I. BMC Bioinformatics. 2010 Sep 29;11:489. doi: 10.1186/1471-2105-11-489, PMID: 20920235 .. [2] CycleFreeFlux: efficient removal of thermodynamically infeasible loops from flux distributions. Desouki AA, Jarre F, Gelius-Dietrich G, Lercher MJ. Bioinformatics. 2015 Jul 1;31(13):2159-65. doi: 10.1093/bioinformatics/btv096. """ if reaction_list is None: reaction_list = model.reactions else: reaction_list = model.reactions.get_by_any(reaction_list) prob = model.problem fva_results = DataFrame({ "minimum": zeros(len(reaction_list), dtype=float), "maximum": zeros(len(reaction_list), dtype=float) }, index=[r.id for r in reaction_list]) with model: # Safety check before setting up FVA. model.slim_optimize(error_value=None, message="There is no optimal solution for the " "chosen objective!") # Add the previous objective as a variable to the model then set it to # zero. This also uses the fraction to create the lower/upper bound for # the old objective. if model.solver.objective.direction == "max": fva_old_objective = prob.Variable( "fva_old_objective", lb=fraction_of_optimum * model.solver.objective.value) else: fva_old_objective = prob.Variable( "fva_old_objective", ub=fraction_of_optimum * model.solver.objective.value) fva_old_obj_constraint = prob.Constraint( model.solver.objective.expression - fva_old_objective, lb=0, ub=0, name="fva_old_objective_constraint") model.add_cons_vars([fva_old_objective, fva_old_obj_constraint]) if pfba_factor is not None: if pfba_factor < 1.: warn("The 'pfba_factor' should be larger or equal to 1.", UserWarning) with model: add_pfba(model, fraction_of_optimum=0) ub = model.slim_optimize(error_value=None) flux_sum = prob.Variable("flux_sum", ub=pfba_factor * ub) flux_sum_constraint = prob.Constraint( model.solver.objective.expression - flux_sum, lb=0, ub=0, name="flux_sum_constraint") model.add_cons_vars([flux_sum, flux_sum_constraint]) model.objective = Zero # This will trigger the reset as well for what in ("minimum", "maximum"): sense = "min" if what == "minimum" else "max" for rxn in reaction_list: # The previous objective assignment already triggers a reset # so directly update coefs here to not trigger redundant resets # in the history manager which can take longer than the actual # FVA for small models model.solver.objective.set_linear_coefficients( {rxn.forward_variable: 1, rxn.reverse_variable: -1}) model.solver.objective.direction = sense model.slim_optimize() sutil.check_solver_status(model.solver.status) if loopless: value = loopless_fva_iter(model, rxn) else: value = model.solver.objective.value fva_results.at[rxn.id, what] = value model.solver.objective.set_linear_coefficients( {rxn.forward_variable: 0, rxn.reverse_variable: 0}) return fva_results
def _fva_optlang(model, reaction_list, fraction, loopless, pfba_factor): """Helper function to perform FVA with the optlang interface. Parameters ---------- model : a cobra model reaction_list : list of reactions fraction : float, optional Must be <= 1.0. Requires that the objective value is at least fraction * max_objective_value. A value of 0.85 for instance means that the objective has to be at least at 85% percent of its maximum. loopless : boolean, optional Whether to return only loopless solutions. pfba_factor : float, optional Add additional constraint to the model that the total sum of absolute fluxes must not be larger than this value times the smallest possible sum of absolute fluxes, i.e., by setting the value to 1.1 then the total sum of absolute fluxes must not be more than 10% larger than the pfba solution. Setting this value may lead to more realistic predictions of the effective flux bounds. Returns ------- dict A dictionary containing the results. """ prob = model.problem fva_results = {rxn.id: {} for rxn in reaction_list} with model as m: m.slim_optimize(error_value=None, message="There is no optimal solution for the " "chosen objective!") # Add objective as a variable to the model than set to zero # This also uses the fraction to create the lower bound for the # old objective fva_old_objective = prob.Variable( "fva_old_objective", lb=fraction * m.solver.objective.value) fva_old_obj_constraint = prob.Constraint( m.solver.objective.expression - fva_old_objective, lb=0, ub=0, name="fva_old_objective_constraint") m.add_cons_vars([fva_old_objective, fva_old_obj_constraint]) if pfba_factor is not None: if pfba_factor < 1.: warn('pfba_factor should be larger or equal to 1', UserWarning) with m: add_pfba(m, fraction_of_optimum=0) ub = m.slim_optimize(error_value=None) flux_sum = prob.Variable("flux_sum", ub=pfba_factor * ub) flux_sum_constraint = prob.Constraint( m.solver.objective.expression - flux_sum, lb=0, ub=0, name="flux_sum_constraint") m.add_cons_vars([flux_sum, flux_sum_constraint]) m.objective = Zero # This will trigger the reset as well for what in ("minimum", "maximum"): sense = "min" if what == "minimum" else "max" for rxn in reaction_list: r_id = rxn.id rxn = m.reactions.get_by_id(r_id) # The previous objective assignment already triggers a reset # so directly update coefs here to not trigger redundant resets # in the history manager which can take longer than the actual # FVA for small models m.solver.objective.set_linear_coefficients( {rxn.forward_variable: 1, rxn.reverse_variable: -1}) m.solver.objective.direction = sense m.slim_optimize() sutil.check_solver_status(m.solver.status) if loopless: value = loopless_fva_iter(m, rxn) else: value = m.solver.objective.value fva_results[r_id][what] = value m.solver.objective.set_linear_coefficients( {rxn.forward_variable: 0, rxn.reverse_variable: 0}) return fva_results
def get_solution(model, reactions=None, metabolites=None, raise_error=False): """ Generate a solution representation of the current solver state. Parameters --------- model : cobra.Model The model whose reactions to retrieve values for. reactions : list, optional An iterable of `cobra.Reaction` objects. Uses `model.reactions` by default. metabolites : list, optional An iterable of `cobra.Metabolite` objects. Uses `model.metabolites` by default. raise_error : bool If true, raise an OptimizationError if solver status is not optimal. Returns ------- cobra.Solution Note ---- This is only intended for the `optlang` solver interfaces and not the legacy solvers. """ check_solver_status(model.solver.status, raise_error=raise_error) if reactions is None: reactions = model.reactions if metabolites is None: metabolites = model.metabolites rxn_index = list() fluxes = empty(len(reactions)) reduced = empty(len(reactions)) var_primals = model.solver.primal_values shadow = empty(len(metabolites)) if model.solver.is_integer: reduced.fill(nan) shadow.fill(nan) for (i, rxn) in enumerate(reactions): rxn_index.append(rxn.id) fluxes[i] = var_primals[rxn.id] - var_primals[rxn.reverse_id] met_index = [met.id for met in metabolites] else: var_duals = model.solver.reduced_costs for (i, rxn) in enumerate(reactions): forward = rxn.id reverse = rxn.reverse_id rxn_index.append(forward) fluxes[i] = var_primals[forward] - var_primals[reverse] reduced[i] = var_duals[forward] - var_duals[reverse] met_index = list() constr_duals = model.solver.shadow_prices for (i, met) in enumerate(metabolites): met_index.append(met.id) shadow[i] = constr_duals[met.id] return Solution(model.solver.objective.value, model.solver.status, Series(index=rxn_index, data=fluxes, name="fluxes"), Series(index=rxn_index, data=reduced, name="reduced_costs"), Series(index=met_index, data=shadow, name="shadow_prices"))
def _fva_optlang(model, reaction_list, fraction, loopless, pfba_factor): """Helper function to perform FVA with the optlang interface. Parameters ---------- model : a cobra model reaction_list : list of reactions fraction : float, optional Must be <= 1.0. Requires that the objective value is at least fraction * max_objective_value. A value of 0.85 for instance means that the objective has to be at least at 85% percent of its maximum. loopless : boolean, optional Whether to return only loopless solutions. pfba_factor : float, optional Add additional constraint to the model that the total sum of absolute fluxes must not be larger than this value times the smallest possible sum of absolute fluxes, i.e., by setting the value to 1.1 then the total sum of absolute fluxes must not be more than 10% larger than the pfba solution. Setting this value may lead to more realistic predictions of the effective flux bounds. Returns ------- dict A dictionary containing the results. """ prob = model.problem fva_results = {rxn.id: {} for rxn in reaction_list} with model as m: m.slim_optimize(error_value=None, message="There is no optimal solution for the " "chosen objective!") # Add objective as a variable to the model than set to zero # This also uses the fraction to create the lower bound for the # old objective fva_old_objective = prob.Variable("fva_old_objective", lb=fraction * m.solver.objective.value) fva_old_obj_constraint = prob.Constraint( m.solver.objective.expression - fva_old_objective, lb=0, ub=0, name="fva_old_objective_constraint") m.add_cons_vars([fva_old_objective, fva_old_obj_constraint]) if pfba_factor is not None: if pfba_factor < 1.: warn('pfba_factor should be larger or equal to 1', UserWarning) with m: add_pfba(m, fraction_of_optimum=0) ub = m.slim_optimize(error_value=None) flux_sum = prob.Variable("flux_sum", ub=pfba_factor * ub) flux_sum_constraint = prob.Constraint( m.solver.objective.expression - flux_sum, lb=0, ub=0, name="flux_sum_constraint") m.add_cons_vars([flux_sum, flux_sum_constraint]) m.objective = Zero # This will trigger the reset as well for what in ("minimum", "maximum"): sense = "min" if what == "minimum" else "max" for rxn in reaction_list: r_id = rxn.id rxn = m.reactions.get_by_id(r_id) # The previous objective assignment already triggers a reset # so directly update coefs here to not trigger redundant resets # in the history manager which can take longer than the actual # FVA for small models m.solver.objective.set_linear_coefficients({ rxn.forward_variable: 1, rxn.reverse_variable: -1 }) m.solver.objective.direction = sense m.slim_optimize() sutil.check_solver_status(m.solver.status) if loopless: value = loopless_fva_iter(m, rxn) else: value = m.solver.objective.value fva_results[r_id][what] = value m.solver.objective.set_linear_coefficients({ rxn.forward_variable: 0, rxn.reverse_variable: 0 }) return fva_results