Source code for cobra.core.solution

# -*- coding: utf-8 -*-

"""Provide unified interfaces to optimization solutions."""

from __future__ import absolute_import

import logging
from builtins import object, super
from warnings import warn

from numpy import empty, nan
from optlang.interface import OPTIMAL
from pandas import DataFrame, Series, option_context

from cobra.util.solver import check_solver_status


__all__ = ("Solution", "LegacySolution", "get_solution")

LOGGER = logging.getLogger(__name__)


[docs]class Solution(object): """ A unified interface to a `cobra.Model` optimization solution. Notes ----- Solution is meant to be constructed by `get_solution` please look at that function to fully understand the `Solution` class. Attributes ---------- objective_value : float The (optimal) value for the objective function. status : str The solver status related to the solution. fluxes : pandas.Series Contains the reaction fluxes (primal values of variables). reduced_costs : pandas.Series Contains reaction reduced costs (dual values of variables). shadow_prices : pandas.Series Contains metabolite shadow prices (dual values of constraints). """ def __init__( self, objective_value, status, fluxes, reduced_costs=None, shadow_prices=None, **kwargs ): """ Initialize a `Solution` from its components. Parameters ---------- objective_value : float The (optimal) value for the objective function. status : str The solver status related to the solution. fluxes : pandas.Series Contains the reaction fluxes (primal values of variables). reduced_costs : pandas.Series Contains reaction reduced costs (dual values of variables). shadow_prices : pandas.Series Contains metabolite shadow prices (dual values of constraints). """ super(Solution, self).__init__(**kwargs) self.objective_value = objective_value self.status = status self.fluxes = fluxes self.reduced_costs = reduced_costs self.shadow_prices = shadow_prices
[docs] def __repr__(self): """String representation of the solution instance.""" if self.status != OPTIMAL: return "<Solution {0:s} at 0x{1:x}>".format(self.status, id(self)) return "<Solution {0:.3f} at 0x{1:x}>".format(self.objective_value, id(self))
[docs] def _repr_html_(self): if self.status == OPTIMAL: with option_context("display.max_rows", 10): html = ( "<strong><em>Optimal</em> solution with objective " "value {:.3f}</strong><br>{}".format( self.objective_value, self.to_frame()._repr_html_() ) ) else: html = "<strong><em>{}</em> solution</strong>".format(self.status) return html
[docs] def __getitem__(self, reaction_id): """ Return the flux of a reaction. Parameters ---------- reaction : str A model reaction ID. """ return self.fluxes[reaction_id]
[docs] get_primal_by_id = __getitem__
[docs] def to_frame(self): """Return the fluxes and reduced costs as a data frame""" return DataFrame({"fluxes": self.fluxes, "reduced_costs": self.reduced_costs})
[docs]class LegacySolution(object): """ Legacy support for an interface to a `cobra.Model` optimization solution. Attributes ---------- f : float The objective value solver : str A string indicating which solver package was used. x : iterable List or Array of the fluxes (primal values). x_dict : dict A dictionary of reaction IDs that maps to the respective primal values. y : iterable List or Array of the dual values. y_dict : dict A dictionary of reaction IDs that maps to the respective dual values. Warning ------- The LegacySolution class and its interface is deprecated. """ def __init__( self, f, x=None, x_dict=None, y=None, y_dict=None, solver=None, the_time=0, status="NA", **kwargs ): """ Initialize a `LegacySolution` from an objective value. Parameters ---------- f : float Objective value. solver : str, optional A string indicating which solver package was used. x : iterable, optional List or Array of the fluxes (primal values). x_dict : dict, optional A dictionary of reaction IDs that maps to the respective primal values. y : iterable, optional List or Array of the dual values. y_dict : dict, optional A dictionary of reaction IDs that maps to the respective dual values. the_time : int, optional status : str, optional .. warning :: deprecated """ super(LegacySolution, self).__init__(**kwargs) self.solver = solver self.f = f self.x = x self.x_dict = x_dict self.status = status self.y = y self.y_dict = y_dict
[docs] def __repr__(self): """String representation of the solution instance.""" if self.status != "optimal": return "<LegacySolution {0:s} at 0x{1:x}>".format(self.status, id(self)) return "<LegacySolution {0:.3f} at 0x{1:x}>".format(self.f, id(self))
[docs] def __getitem__(self, reaction_id): """ Return the flux of a reaction. Parameters ---------- reaction_id : str A reaction ID. """ return self.x_dict[reaction_id]
[docs] def dress_results(self, model): """ Method could be intended as a decorator. .. warning :: deprecated """ warn("unnecessary to call this deprecated function", DeprecationWarning)
[docs]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"),
)