def get_solution(self): """ Overrides the cobra.thermo.solution method, to also get the supplementary variables we added to the cobra_model :return: """ objective_value = self.solver.objective.value status = self.solver.status variables = pd.Series(data=self.solver.primal_values) solution = Solution(objective_value=objective_value, status=status, fluxes=variables) return solution
def get_solution(self): """ Overrides the cobra.thermo.solution method, to also get the supplementary variables we added to the cobra_model * :code:`solution.fluxes` in `cobrapy` is a transformed version of the solver output, as it actually calculates the _net_ flux of each reaction by substracting the reverse variable value to the forward variable value. This should be used anytime one needs the actual flux value * :code:`solution.raw` is a clear copy of the solver output. From there one can access the value at solution for all the variables of the problem. However, looking for a reaction ID in there will only give the _forward_ flux. This should be used for any other variable than fluxes. * :code:`solution.values` yields variables multiplied by their scaling factor (1 by default). Useful if you operated scaling on your equations for numerical reasons. This does _not_ include fluxes :return: """ objective_value = self.solver.objective.value status = self.solver.status variables = pd.Series(data=self.solver.primal_values) fluxes = empty(len(self.reactions)) rxn_index = list() var_primals = self.solver.primal_values for (i, rxn) in enumerate(self.reactions): rxn_index.append(rxn.id) fluxes[i] = var_primals[rxn.id] - var_primals[rxn.reverse_id] fluxes = pd.Series(index=rxn_index, data=fluxes, name="fluxes") solution = Solution(objective_value=objective_value, status=status, fluxes=fluxes) self.solution = solution self.solution.raw = variables self.\ solution.values = pd.DataFrame.from_dict({k:v.unscaled for k,v in self._var_dict.items()}, orient = 'index') return solution
def MergeRev(model, update_solution=True): ###modify.revert_to_reversible(model, update_solution=update_solution) """ The cobra function above fails to update the solution object when merging. This results in how reactions that goes in the reversible direction to be missing from solution. Fix is made by commenting the remove_reactions function and, instead, changing the solution dict manually in GetSol. """ ### Copied cobra code here reverse_reactions = [ x for x in model.reactions if "reflection" in x.notes and x.id.endswith('_reverse') ] # If there are no reverse reactions, then there is nothing to do if len(reverse_reactions) == 0: return if update_solution: if isinstance(model.solution, Solution): fluxes = dict(model.solution.fluxes) reduced = dict(model.solution.reduced_costs) for reverse in reverse_reactions: forward = reverse.reflection if reverse.id in fluxes.keys(): fluxes[forward.id] -= fluxes.pop(reverse.id) reduced[forward.id] -= reduced.pop(reverse.id) merged_sol = Solution( model.solution.objective_value, model.solution.status, Series(index=fluxes.keys(), data=fluxes.values(), name="fluxes"), Series(index=reduced.keys(), data=reduced.values(), name="reduced_costs"), model.solution.shadow_prices) model.UpdateSolution(merged_sol) for reverse in reverse_reactions: forward_id = reverse.notes.pop("reflection") forward = model.reactions.get_by_id(forward_id) forward.lower_bound = -reverse.upper_bound if forward.upper_bound == 0: forward.upper_bound = -reverse.lower_bound if "reflection" in forward.notes: forward.notes.pop("reflection") model.remove_reactions(reverse_reactions)
def SplitRev(model, split_solution=True): #modify.convert_to_irreversible(model) reactions_to_add = [] coefficients = {} for reaction in model.reactions: # If a reaction is reverse only, the forward reaction (which # will be constrained to 0) will be left in the model. if reaction.lower_bound < 0: reverse_reaction = Reaction(reaction.id + "_reverse") reverse_reaction.lower_bound = min(0, reaction.upper_bound) * -1 reverse_reaction.upper_bound = reaction.lower_bound * -1 coefficients[ reverse_reaction] = reaction.objective_coefficient * -1 reaction.lower_bound = 0 reaction.upper_bound = max(0, reaction.upper_bound) #Make the directions aware of each other reaction.reflection = reverse_reaction reverse_reaction.reflection = reaction reaction.notes["reflection"] = reverse_reaction.id reverse_reaction.notes["reflection"] = reaction.id reaction_dict = { k: v * -1 for k, v in iteritems(reaction._metabolites) } reverse_reaction.add_metabolites(reaction_dict) reverse_reaction._model = reaction._model reverse_reaction._genes = reaction._genes for gene in reaction._genes: gene._reaction.add(reverse_reaction) reverse_reaction._gene_reaction_rule = reaction._gene_reaction_rule reverse_reaction.subsystem = reaction.subsystem reactions_to_add.append(reverse_reaction) model.add_reactions(reactions_to_add) model.SetObjective(coefficients) if split_solution: reverse_reactions = [ x for x in model.reactions if "reflection" in x.notes and x.id.endswith('_reverse') ] if isinstance(model.solution, Solution): fluxes = dict(model.solution.fluxes) reduced = dict(model.solution.reduced_costs) for reverse in reverse_reactions: forward = reverse.reflection if (forward.id in fluxes) and (reverse.id in fluxes): if fluxes[forward.id] < 0: fluxes[reverse.id] = -fluxes[forward.id] fluxes[forward.id] = 0 else: fluxes[reverse.id] = 0 if reduced[forward.id] < 0: reduced[reverse.id] = -reduced[forward.id] reduced[forward.id] = 0 else: reduced[reverse.id] = 0 # else: # print(forward.id + ' and ' + reverse.id + ' not in solution') split_sol = Solution( model.solution.objective_value, model.solution.status, Series(index=fluxes.keys(), data=fluxes.values(), name="fluxes"), Series(index=reduced.keys(), data=reduced.values(), name="reduced_costs"), model.solution.shadow_prices) model.UpdateSolution(split_sol)
def simulate(self, objective=None, method=SimulationMethod.FBA, maximize=True, constraints=None, reference=None, scalefactor=None): ''' Simulates a phenotype when applying a set constraints using the specified method. :param dic objective: The simulation objective. If none, the model objective is considered. :param method: The SimulationMethod (FBA, pFBA, lMOMA, etc ...) :param boolean maximize: The optimization direction. :param dic constraints: A dictionary of constraints to be applied to the model. :param dic reference: A dictionary of reaction flux values. :param float scalefactor: A positive scaling factor for the solver. Default None. ''' if not objective: objective = self.model.objective elif isinstance(objective, dict) and len(objective) > 0: objective = next(iter(objective.keys())) simul_constraints = OrderedDict() if constraints: simul_constraints.update(constraints) if self.constraints: simul_constraints.update(self.constraints) if self.environmental_conditions: simul_constraints.update(self.environmental_conditions) with self.model as model: model.objective = objective for rxn in list(simul_constraints.keys()): reac = model.reactions.get_by_id(rxn) # constraints defined as a tuple (lower_bound, upper_bound) or as a single float value if isinstance(simul_constraints.get(rxn), tuple): reac.bounds = (simul_constraints.get( rxn)[0], simul_constraints.get(rxn)[1]) else: reac.bounds = (simul_constraints.get( rxn), simul_constraints.get(rxn)) # NOTE: If working directly over optlang use 'max' and 'min' # such is the case with pytfa.core.Model... need to find some workaround objective_sense = 'maximize' if maximize else 'minimize' if method == SimulationMethod.FBA: solution = model.optimize(objective_sense=objective_sense) elif method == SimulationMethod.pFBA: # make fraction_of_optimum a configurable parameter? solution = pfba(model) elif method == SimulationMethod.lMOMA or method == SimulationMethod.MOMA: s = None if not maximize: s = model.optimize(objective_sense=objective_sense) linear = True if method == SimulationMethod.lMOMA else False solution = moma(model, solution=s, linear=linear) elif method == SimulationMethod.ROOM: solution = room(model) # Special case in which only the simulation context is required without any simulation result elif method == SimulationMethod.NONE: solution = Solution(None, 'unknown', None) pass else: raise Exception( "Unknown method to perform the simulation.") status = self.__status_mapping[solution.status] result = SimulationResult(model, solution.objective_value, fluxes=solution.fluxes.to_dict(OrderedDict), status=status, envcond=self.environmental_conditions, model_constraints=self.constraints, simul_constraints=constraints, maximize=maximize) return result