def _add_switches(self, reactions): logger.info("Adding switches.") y_vars = list() switches = list() self._exchanges = list() for reaction in reactions: if reaction.id.startswith('DM_'): # demand reactions don't need integer switches self._exchanges.append(reaction) continue y = self.model.solver.interface.Variable('y_' + reaction.id, lb=0, ub=1, type='binary') y_vars.append(y) # The following is a complicated but efficient way to write the following constraints # switch_lb = self.model.solver.interface.Constraint(y * reaction.lower_bound - reaction.flux_expression, # name='switch_lb_' + reaction.id, ub=0) # switch_ub = self.model.solver.interface.Constraint(y * reaction.upper_bound - reaction.flux_expression, # name='switch_ub_' + reaction.id, lb=0) forward_var_term = Mul._from_args((RealNumber(-1), reaction.forward_variable)) reverse_var_term = Mul._from_args((RealNumber(-1), reaction.reverse_variable)) switch_lb_y_term = Mul._from_args((RealNumber(reaction.lower_bound), y)) switch_ub_y_term = Mul._from_args((RealNumber(reaction.upper_bound), y)) switch_lb = self.model.solver.interface.Constraint( Add._from_args((switch_lb_y_term, forward_var_term, reverse_var_term)), name='switch_lb_' + reaction.id, ub=0, sloppy=True) switch_ub = self.model.solver.interface.Constraint( Add._from_args((switch_ub_y_term, forward_var_term, reverse_var_term)), name='switch_ub_' + reaction.id, lb=0, sloppy=True) switches.extend([switch_lb, switch_ub]) self.model.solver.add(y_vars) self.model.solver.add(switches, sloppy=True) logger.info("Setting minimization of switch variables as objective.") self.model.objective = self.model.solver.interface.Objective(Add(*y_vars), direction='min') self._y_vars_ids = [var.name for var in y_vars]
def remove_infeasible_cycles(model, fluxes, fix=()): """Remove thermodynamically infeasible cycles from a flux distribution. Parameters --------- model : cobra.Model The model that generated the flux distribution. fluxes : dict The flux distribution containing infeasible loops. Returns ------- dict A cycle free flux distribution. References ---------- .. [1] A. A. Desouki, F. Jarre, G. Gelius-Dietrich, and M. J. Lercher, “CycleFreeFlux: efficient removal of thermodynamically infeasible loops from flux distributions.” """ with model: # make sure the original object is restored exchange_reactions = model.boundary exchange_ids = [exchange.id for exchange in exchange_reactions] internal_reactions = [reaction for reaction in model.reactions if reaction.id not in exchange_ids] for exchange in exchange_reactions: exchange_flux = fluxes[exchange.id] exchange.bounds = (exchange_flux, exchange_flux) cycle_free_objective_list = [] for internal_reaction in internal_reactions: internal_flux = fluxes[internal_reaction.id] if internal_flux >= 0: cycle_free_objective_list.append(Mul._from_args((FloatOne, internal_reaction.forward_variable))) internal_reaction.bounds = (0, internal_flux) else: # internal_flux < 0: cycle_free_objective_list.append(Mul._from_args((FloatOne, internal_reaction.reverse_variable))) internal_reaction.bounds = (internal_flux, 0) cycle_free_objective = model.solver.interface.Objective( Add._from_args(cycle_free_objective_list), direction="min", sloppy=True ) model.objective = cycle_free_objective for reaction_id in fix: reaction_to_fix = model.reactions.get_by_id(reaction_id) reaction_to_fix.bounds = (fluxes[reaction_id], fluxes[reaction_id]) try: solution = model.optimize(raise_error=True) except OptimizationError as e: logger.warning("Couldn't remove cycles from reference flux distribution.") raise e result = solution.fluxes return result
def remove_infeasible_cycles(model, fluxes, fix=()): """Remove thermodynamically infeasible cycles from a flux distribution. Arguments --------- model : cobra.Model The model that generated the flux distribution. fluxes : dict The flux distribution containing infeasible loops. Returns ------- dict A cycle free flux distribution. References ---------- .. [1] A. A. Desouki, F. Jarre, G. Gelius-Dietrich, and M. J. Lercher, “CycleFreeFlux: efficient removal of thermodynamically infeasible loops from flux distributions.” """ with model: # make sure the original object is restored exchange_reactions = model.boundary exchange_ids = [exchange.id for exchange in exchange_reactions] internal_reactions = [reaction for reaction in model.reactions if reaction.id not in exchange_ids] for exchange in exchange_reactions: exchange_flux = fluxes[exchange.id] exchange.bounds = (exchange_flux, exchange_flux) cycle_free_objective_list = [] for internal_reaction in internal_reactions: internal_flux = fluxes[internal_reaction.id] if internal_flux >= 0: cycle_free_objective_list.append(Mul._from_args((FloatOne, internal_reaction.forward_variable))) internal_reaction.bounds = (0, internal_flux) else: # internal_flux < 0: cycle_free_objective_list.append(Mul._from_args((FloatOne, internal_reaction.reverse_variable))) internal_reaction.bounds = (internal_flux, 0) cycle_free_objective = model.solver.interface.Objective( Add._from_args(cycle_free_objective_list), direction="min", sloppy=True ) model.objective = cycle_free_objective for reaction_id in fix: reaction_to_fix = model.reactions.get_by_id(reaction_id) reaction_to_fix.bounds = (fluxes[reaction_id], fluxes[reaction_id]) try: solution = model.optimize(raise_error=True) except OptimizationError as e: logger.warning("Couldn't remove cycles from reference flux distribution.") raise e result = solution.fluxes return result
def _add_switches(self, reactions): logger.info("Adding switches.") y_vars = list() switches = list() self._exchanges = list() for reaction in reactions: if reaction.id.startswith('DM_'): # demand reactions don't need integer switches self._exchanges.append(reaction) continue y = self.model.solver.interface.Variable('y_' + reaction.id, lb=0, ub=1, type='binary') y_vars.append(y) # The following is a complicated but efficient way to write the following constraints # switch_lb = self.model.solver.interface.Constraint(y * reaction.lower_bound - reaction.flux_expression, # name='switch_lb_' + reaction.id, ub=0) # switch_ub = self.model.solver.interface.Constraint(y * reaction.upper_bound - reaction.flux_expression, # name='switch_ub_' + reaction.id, lb=0) forward_var_term = Mul._from_args( (RealNumber(-1), reaction.forward_variable)) reverse_var_term = Mul._from_args( (RealNumber(-1), reaction.reverse_variable)) switch_lb_y_term = Mul._from_args( (RealNumber(reaction.lower_bound), y)) switch_ub_y_term = Mul._from_args( (RealNumber(reaction.upper_bound), y)) switch_lb = self.model.solver.interface.Constraint( Add._from_args( (switch_lb_y_term, forward_var_term, reverse_var_term)), name='switch_lb_' + reaction.id, ub=0, sloppy=True) switch_ub = self.model.solver.interface.Constraint( Add._from_args( (switch_ub_y_term, forward_var_term, reverse_var_term)), name='switch_ub_' + reaction.id, lb=0, sloppy=True) switches.extend([switch_lb, switch_ub]) self.model.solver.add(y_vars) self.model.solver.add(switches, sloppy=True) logger.info("Setting minimization of switch variables as objective.") self.model.objective = self.model.solver.interface.Objective( Add(*y_vars), direction='min') self._y_vars_ids = [var.name for var in y_vars]
def _print_Mul(self, expr): if _coeff_isneg(expr): x = self.dom.createElement('apply') x.appendChild(self.dom.createElement('minus')) x.appendChild(self._print_Mul(-expr)) return x from sympy.simplify import fraction numer, denom = fraction(expr) if denom is not S.One: x = self.dom.createElement('apply') x.appendChild(self.dom.createElement('divide')) x.appendChild(self._print(numer)) x.appendChild(self._print(denom)) return x coeff, terms = expr.as_coeff_mul() if coeff is S.One and len(terms) == 1: # XXX since the negative coefficient has been handled, I don't # think a coeff of 1 can remain return self._print(terms[0]) if self.order != 'old': terms = Mul._from_args(terms).as_ordered_factors() x = self.dom.createElement('apply') x.appendChild(self.dom.createElement('times')) if(coeff != 1): x.appendChild(self._print(coeff)) for term in terms: x.appendChild(self._print(term)) return x
def _eval_expand_expectation(self, **hints): A = self.args[0] if isinstance(A, Add): # <A + B> = <A> + <B> return Add(*(Expectation(a, self.is_normal_order).expand( expectation=True) for a in A.args)) if isinstance(A, Mul): # <c A> = c<A> where c is a commutative term A = A.expand() cA, ncA = A.args_cnc() return Mul( Mul(*cA), Expectation(Mul._from_args(ncA), self.is_normal_order).expand()) if isinstance(A, Integral): # <∫adx> -> ∫<a>dx func, lims = A.function, A.limits new_args = [Expectation(func, self.is_normal_order).expand()] for lim in lims: new_args.append(lim) return Integral(*new_args) return self
def multiply(expr, mrow): from sympy.simplify import fraction numer, denom = fraction(expr) if denom is not S.One: frac = self.dom.createElement('mfrac') xnum = self._print(numer) xden = self._print(denom) frac.appendChild(xnum) frac.appendChild(xden) return frac coeff, terms = expr.as_coeff_mul() if coeff is S.One and len(terms) == 1: return self._print(terms[0]) if self.order != 'old': terms = Mul._from_args(terms).as_ordered_factors() if(coeff != 1): x = self._print(coeff) y = self.dom.createElement('mo') y.appendChild(self.dom.createTextNode(self.mathml_tag(expr))) mrow.appendChild(x) mrow.appendChild(y) for term in terms: x = self._print(term) mrow.appendChild(x) if not term == terms[-1]: y = self.dom.createElement('mo') y.appendChild(self.dom.createTextNode(self.mathml_tag(expr))) mrow.appendChild(y) return mrow
def multiply(expr, mrow): from sympy.simplify import fraction numer, denom = fraction(expr) if denom is not S.One: frac = self.dom.createElement('mfrac') if self._settings["fold_short_frac"] and len(str(expr)) < 7: frac.setAttribute('bevelled', 'true') xnum = self._print(numer) xden = self._print(denom) frac.appendChild(xnum) frac.appendChild(xden) mrow.appendChild(frac) return mrow coeff, terms = expr.as_coeff_mul() if coeff is S.One and len(terms) == 1: mrow.appendChild(self._print(terms[0])) return mrow if self.order != 'old': terms = Mul._from_args(terms).as_ordered_factors() if coeff != 1: x = self._print(coeff) y = self.dom.createElement('mo') y.appendChild(self.dom.createTextNode(self.mathml_tag(expr))) mrow.appendChild(x) mrow.appendChild(y) for term in terms: x = self._print(term) mrow.appendChild(x) if not term == terms[-1]: y = self.dom.createElement('mo') y.appendChild(self.dom.createTextNode(self.mathml_tag(expr))) mrow.appendChild(y) return mrow
def eval(cls, a, b): if not (a and b): return S.Zero if a == b: return Integer(2)*a**2 if a.is_commutative or b.is_commutative: return Integer(2)*a*b # [xA,yB] -> xy*[A,B] # from sympy.physics.qmul import QMul ca, nca = a.args_cnc() cb, ncb = b.args_cnc() c_part = ca + cb if c_part: return Mul(Mul(*c_part), cls(Mul._from_args(nca), Mul._from_args(ncb))) # Canonical ordering of arguments #The Commutator [A,B] is on canonical form if A < B. if a.compare(b) == 1: return cls(b,a)
def flatten(cls, args): # TODO: disallow nested TensorProducts. c_part = [] nc_parts = [] for arg in args: cp, ncp = arg.args_cnc() c_part.extend(list(cp)) nc_parts.append(Mul._from_args(ncp)) return c_part, nc_parts
def eval(cls, a, b): if not (a and b): return S.Zero if a == b: return S.Zero if a.is_commutative or b.is_commutative: return S.Zero # [xA,yB] -> xy*[A,B] ca, nca = a.args_cnc() cb, ncb = b.args_cnc() c_part = ca + cb if c_part: return Mul(Mul(*c_part), cls(Mul._from_args(nca), Mul._from_args(ncb))) # Canonical ordering of arguments # The Commutator [A, B] is in canonical form if A < B. if a.compare(b) == 1: return S.NegativeOne*cls(b, a)
def eval(cls, a, b): """The Commutator [A,B] is on canonical form if A < B. """ if not (a and b): return S.Zero if a == b: return S.Zero if a.is_commutative or b.is_commutative: return S.Zero # [xA,yB] -> xy*[A,B] # from sympy.physics.qmul import QMul ca, nca = a.args_cnc() cb, ncb = b.args_cnc() c_part = list(ca) + list(cb) if c_part: return Mul(Mul(*c_part), cls(Mul._from_args(nca), Mul._from_args(ncb))) # Canonical ordering of arguments if a.compare(b) == 1: return S.NegativeOne*cls(b,a)
def eval(cls, a, b): """The Commutator [A,B] is on canonical form if A < B. """ if not (a and b): return S.Zero if a == b: return S.Zero if a.is_commutative or b.is_commutative: return S.Zero # [xA,yB] -> xy*[A,B] # from sympy.physics.qmul import QMul ca, nca = a.args_cnc() cb, ncb = b.args_cnc() c_part = list(ca) + list(cb) if c_part: return Mul(Mul(*c_part), cls(Mul._from_args(nca), Mul._from_args(ncb))) # Canonical ordering of arguments if a.compare(b) == 1: return S.NegativeOne * cls(b, a)
def _eval_expand_covariance(self, **hints): A, B = self.args[0], self.args[1] # <A + B, C> = <A, C> + <B, C> if isinstance(A, Add): return Add(*(Covariance(a, B, self.is_normal_order).expand() for a in A.args)) # <A, B + C> = <A, B> + <A, C> if isinstance(B, Add): return Add(*(Covariance(A, b, self.is_normal_order).expand() for b in B.args)) if isinstance(A, Mul): A = A.expand() cA, ncA = A.args_cnc() return Mul( Mul(*cA), Covariance(Mul._from_args(ncA), B, self.is_normal_order).expand()) if isinstance(B, Mul): B = B.expand() cB, ncB = B.args_cnc() return Mul( Mul(*cB), Covariance(A, Mul._from_args(ncB), self.is_normal_order).expand()) if isinstance(A, Integral): # <∫adx, B> -> ∫<a, B>dx func, lims = A.function, A.limits new_args = [Covariance(func, B, self.is_normal_order).expand()] for lim in lims: new_args.append(lim) return Integral(*new_args) if isinstance(B, Integral): # <A, ∫bdx> -> ∫<A, b>dx func, lims = B.function, B.limits new_args = [Covariance(A, func, self.is_normal_order).expand()] for lim in lims: new_args.append(lim) return Integral(*new_args) return self
def _eval_expand_covariance(self, **hints): A, B = self.args[0], self.args[1] # <A + B, C> = <A, C> + <B, C> if isinstance(A, Add): return Add(*(Covariance(a, B, self.is_normal_order).expand() for a in A.args)) # <A, B + C> = <A, B> + <A, C> if isinstance(B, Add): return Add(*(Covariance(A, b, self.is_normal_order).expand() for b in B.args)) if isinstance(A, Mul): A = A.expand() cA, ncA = A.args_cnc() return Mul(Mul(*cA), Covariance(Mul._from_args(ncA), B, self.is_normal_order).expand()) if isinstance(B, Mul): B = B.expand() cB, ncB = B.args_cnc() return Mul(Mul(*cB), Covariance(A, Mul._from_args(ncB), self.is_normal_order).expand()) if isinstance(A, Integral): # <∫adx, B> -> ∫<a, B>dx func, lims = A.function, A.limits new_args = [Covariance(func, B, self.is_normal_order).expand()] for lim in lims: new_args.append(lim) return Integral(*new_args) if isinstance(B, Integral): # <A, ∫bdx> -> ∫<A, b>dx func, lims = B.function, B.limits new_args = [Covariance(A, func, self.is_normal_order).expand()] for lim in lims: new_args.append(lim) return Integral(*new_args) return self
def linear_expand(expr): """ linear_expand takes an expression that is the sum of a scalar expression and a linear combination of noncommutative terms with scalar coefficients and generates lists of coefficients and noncommutative symbols the coefficients multiply. The list of noncommutatives symbols contains the scalar 1 if there is a scalar term in the sum and also does not contain any repeated noncommutative symbols. """ if not isinstance(expr, Expr): raise TypeError('{!r} is not a SymPy Expr'.format(expr)) expr = expand(expr) if expr == 0: coefs = [expr] bases = [S(1)] return (coefs, bases) if isinstance(expr, Add): args = expr.args else: if expr.is_commutative: return ([expr], [S(1)]) else: args = [expr] coefs = [] bases = [] for term in args: if term.is_commutative: if S(1) in bases: coefs[bases.index(S(1))] += term else: bases.append(S(1)) coefs.append(term) else: c, nc = term.args_cnc() base = nc[0] coef = Mul._from_args(c) if base in bases: coefs[bases.index(base)] += coef else: bases.append(base) coefs.append(coef) return (coefs, bases)
def _eval_expand_expectation(self, **hints): A = self.args[0] if isinstance(A, Add): # <A + B> = <A> + <B> return Add(*(Expectation(a, self.is_normal_order).expand(expectation=True) for a in A.args)) if isinstance(A, Mul): # <c A> = c<A> where c is a commutative term A = A.expand() cA, ncA = A.args_cnc() return Mul(Mul(*cA), Expectation(Mul._from_args(ncA), self.is_normal_order).expand()) if isinstance(A, Integral): # <∫adx> -> ∫<a>dx func, lims = A.function, A.limits new_args = [Expectation(func, self.is_normal_order).expand()] for lim in lims: new_args.append(lim) return Integral(*new_args) return self
def linear_expand(expr, mode=True): if isinstance(expr, Expr): expr = expand(expr) if expr == 0: coefs = [expr] bases = [S(1)] return (coefs, bases) if isinstance(expr, Add): args = expr.args else: if expr.is_commutative: return ([expr], [S(1)]) else: args = [expr] coefs = [] bases = [] for term in args: if term.is_commutative: if S(1) in bases: coefs[bases.index(S(1))] += term else: bases.append(S(1)) coefs.append(term) else: c, nc = term.args_cnc() base = nc[0] coef = Mul._from_args(c) if base in bases: coefs[bases.index(base)] += coef else: bases.append(base) coefs.append(coef) if mode: return (coefs, bases) else: return list(zip(coefs, bases))
def linear_expand(expr, mode=True): if isinstance(expr, Expr): expr = expand(expr) if expr == 0: coefs = [expr] bases = [S(1)] return (coefs, bases) if isinstance(expr, Add): args = expr.args else: if expr.is_commutative: return ([expr], [S(1)]) else: args = [expr] coefs = [] bases = [] for term in args: if term.is_commutative: if S(1) in bases: coefs[bases.index(S(1))] += term else: bases.append(S(1)) coefs.append(term) else: c, nc = term.args_cnc() base = nc[0] coef = Mul._from_args(c) if base in bases: coefs[bases.index(base)] += coef else: bases.append(base) coefs.append(coef) if mode: return (coefs, bases) else: return zip(coefs, bases)
def linear_expand(expr): if not isinstance(expr, Expr): raise TypeError('{!r} is not a SymPy Expr'.format(expr)) expr = expand(expr) if expr == 0: coefs = [expr] bases = [S(1)] return (coefs, bases) if isinstance(expr, Add): args = expr.args else: if expr.is_commutative: return ([expr], [S(1)]) else: args = [expr] coefs = [] bases = [] for term in args: if term.is_commutative: if S(1) in bases: coefs[bases.index(S(1))] += term else: bases.append(S(1)) coefs.append(term) else: c, nc = term.args_cnc() base = nc[0] coef = Mul._from_args(c) if base in bases: coefs[bases.index(base)] += coef else: bases.append(base) coefs.append(coef) return (coefs, bases)
def _print_Mul(self, expr): if _coeff_isneg(expr): x = _mathml_comp.RowComponent() x.append_object( _mathml_comp.OperatorComponent(_mathml_comp.OPERATOR_MINUS)) x.append_object(self._print_Mul(-expr)) return x PREC = precedence(expr) from sympy.simplify import fraction numer, denom = fraction(expr) if denom is not S.One: return _mathml_comp.FractionComponent(self._print(numer), self._print(denom)) coeff, terms = expr.as_coeff_mul() if coeff is S.One and len(terms) == 1: # Since the negative coefficient has been handled, I don't # thing a coeff of 1 can remain if precedence(terms[0]) < PREC: # Return the argument with parentheses around. tmp_node = _mathml_comp.RowComponent() tmp_node.append_object( _mathml_comp.OperatorComponent( _mathml_comp.OPERATOR_LEFT_PARENTHESIS)) tmp_node.append_object(self._print(terms[0])) tmp_node.append_object( _mathml_comp.OperatorComponent( _mathml_comp.OPERATOR_RIGHT_PARENTHESIS)) return tmp_node else: # Return the argument only. return self._print(terms[0]) if self.order != 'old': # noinspection PyProtectedMember terms = Mul._from_args(terms).as_ordered_factors() # Build result row element(node). x = _mathml_comp.RowComponent() if coeff != 1: if precedence(coeff) < PREC: # Insert the coefficient number with parentheses around. x.append_object( _mathml_comp.OperatorComponent( _mathml_comp.OPERATOR_LEFT_PARENTHESIS)) x.append_object(self._print(coeff)) x.append_object( _mathml_comp.OperatorComponent( _mathml_comp.OPERATOR_RIGHT_PARENTHESIS)) else: # Insert the coefficient number only. x.append_object(self._print(coeff)) # Insert a multiply operator. if not terms[0].is_Symbol: x.append_object( _mathml_comp.OperatorComponent( _mathml_comp.OPERATOR_MULTIPLY)) terms_len = len(terms) for term_id in range(0, terms_len): cur_term = terms[term_id] if precedence(cur_term) < PREC: x.append_object( _mathml_comp.OperatorComponent( _mathml_comp.OPERATOR_LEFT_PARENTHESIS)) x.append_object(self._print(cur_term)) x.append_object( _mathml_comp.OperatorComponent( _mathml_comp.OPERATOR_RIGHT_PARENTHESIS)) else: x.append_object(self._print(cur_term)) if term_id + 1 != terms_len and not cur_term.is_Symbol: x.append_object( _mathml_comp.OperatorComponent( _mathml_comp.OPERATOR_MULTIPLY)) return x
def remove_infeasible_cycles(model, fluxes, fix=()): """Remove thermodynamically infeasible cycles from a flux distribution. Arguments --------- model : SolverBasedModel The model that generated the flux distribution. fluxes : dict The flux distribution containing infeasible loops. Returns ------- dict A cycle free flux distribution. References ---------- .. [1] A. A. Desouki, F. Jarre, G. Gelius-Dietrich, and M. J. Lercher, “CycleFreeFlux: efficient removal of thermodynamically infeasible loops from flux distributions.” """ with TimeMachine() as tm: # make sure the original object is restored tm(do=int, undo=partial(setattr, model, 'objective', model.objective)) exchange_reactions = model.exchanges exchange_ids = [exchange.id for exchange in exchange_reactions] internal_reactions = [ reaction for reaction in model.reactions if reaction.id not in exchange_ids ] for exchange in exchange_reactions: exchange_flux = fluxes[exchange.id] tm(do=partial(setattr, exchange, 'lower_bound', exchange_flux), undo=partial(setattr, exchange, 'lower_bound', exchange.lower_bound)) tm(do=partial(setattr, exchange, 'upper_bound', exchange_flux), undo=partial(setattr, exchange, 'upper_bound', exchange.upper_bound)) cycle_free_objective_list = [] for internal_reaction in internal_reactions: internal_flux = fluxes[internal_reaction.id] if internal_flux >= 0: cycle_free_objective_list.append( Mul._from_args( (FloatOne, internal_reaction.forward_variable))) tm(do=partial(setattr, internal_reaction, 'lower_bound', 0), undo=partial(setattr, internal_reaction, 'lower_bound', internal_reaction.lower_bound)) tm(do=partial(setattr, internal_reaction, 'upper_bound', internal_flux), undo=partial(setattr, internal_reaction, 'upper_bound', internal_reaction.upper_bound)) else: # internal_flux < 0: cycle_free_objective_list.append( Mul._from_args( (FloatOne, internal_reaction.reverse_variable))) tm(do=partial(setattr, internal_reaction, 'lower_bound', internal_flux), undo=partial(setattr, internal_reaction, 'lower_bound', internal_reaction.lower_bound)) tm(do=partial(setattr, internal_reaction, 'upper_bound', 0), undo=partial(setattr, internal_reaction, 'upper_bound', internal_reaction.upper_bound)) cycle_free_objective = model.solver.interface.Objective( Add._from_args(cycle_free_objective_list), direction="min", sloppy=True) model.objective = cycle_free_objective for reaction_id in fix: reaction_to_fix = model.reactions.get_by_id(reaction_id) tm(do=partial(setattr, reaction_to_fix, 'lower_bound', fluxes[reaction_id]), undo=partial(setattr, reaction_to_fix, 'lower_bound', reaction_to_fix.lower_bound)) tm(do=partial(setattr, reaction_to_fix, 'upper_bound', fluxes[reaction_id]), undo=partial(setattr, reaction_to_fix, 'upper_bound', reaction_to_fix.upper_bound)) try: solution = model.solve() except SolveError as e: logger.warning( "Couldn't remove cycles from reference flux distribution.") raise e result = solution.x_dict return result
def remove_infeasible_cycles(model, fluxes, fix=()): """Remove thermodynamically infeasible cycles from a flux distribution. Arguments --------- model : SolverBasedModel The model that generated the flux distribution. fluxes : dict The flux distribution containing infeasible loops. Returns ------- dict A cycle free flux distribution. References ---------- .. [1] A. A. Desouki, F. Jarre, G. Gelius-Dietrich, and M. J. Lercher, “CycleFreeFlux: efficient removal of thermodynamically infeasible loops from flux distributions.” """ with TimeMachine() as tm: # make sure the original object is restored tm(do=int, undo=partial(setattr, model, 'objective', model.objective)) exchange_reactions = model.exchanges exchange_ids = [exchange.id for exchange in exchange_reactions] internal_reactions = [reaction for reaction in model.reactions if reaction.id not in exchange_ids] for exchange in exchange_reactions: exchange_flux = fluxes[exchange.id] tm(do=partial(setattr, exchange, 'lower_bound', exchange_flux), undo=partial(setattr, exchange, 'lower_bound', exchange.lower_bound)) tm(do=partial(setattr, exchange, 'upper_bound', exchange_flux), undo=partial(setattr, exchange, 'upper_bound', exchange.upper_bound)) cycle_free_objective_list = [] for internal_reaction in internal_reactions: internal_flux = fluxes[internal_reaction.id] if internal_flux >= 0: cycle_free_objective_list.append(Mul._from_args((FloatOne, internal_reaction.forward_variable))) tm(do=partial(setattr, internal_reaction, 'lower_bound', 0), undo=partial(setattr, internal_reaction, 'lower_bound', internal_reaction.lower_bound)) tm(do=partial(setattr, internal_reaction, 'upper_bound', internal_flux), undo=partial(setattr, internal_reaction, 'upper_bound', internal_reaction.upper_bound)) else: # internal_flux < 0: cycle_free_objective_list.append(Mul._from_args((FloatOne, internal_reaction.reverse_variable))) tm(do=partial(setattr, internal_reaction, 'lower_bound', internal_flux), undo=partial(setattr, internal_reaction, 'lower_bound', internal_reaction.lower_bound)) tm(do=partial(setattr, internal_reaction, 'upper_bound', 0), undo=partial(setattr, internal_reaction, 'upper_bound', internal_reaction.upper_bound)) cycle_free_objective = model.solver.interface.Objective( Add._from_args(cycle_free_objective_list), direction="min", sloppy=True ) model.objective = cycle_free_objective for reaction_id in fix: reaction_to_fix = model.reactions.get_by_id(reaction_id) tm(do=partial(setattr, reaction_to_fix, 'lower_bound', fluxes[reaction_id]), undo=partial(setattr, reaction_to_fix, 'lower_bound', reaction_to_fix.lower_bound)) tm(do=partial(setattr, reaction_to_fix, 'upper_bound', fluxes[reaction_id]), undo=partial(setattr, reaction_to_fix, 'upper_bound', reaction_to_fix.upper_bound)) try: solution = model.solve() except SolveError as e: logger.warning("Couldn't remove cycles from reference flux distribution.") raise e result = solution.x_dict return result