def _cycle_free_fva(model, reactions=None, sloppy=True, sloppy_bound=666): """Cycle free flux-variability analysis. (http://cran.r-project.org/web/packages/sybilcycleFreeFlux/index.html) Parameters ---------- model : SolverBasedModel reactions : list List of reactions whose flux-ranges should be determined. sloppy : boolean, optional If true, only fluxes v with abs(v) > sloppy_bound are checked to be futile cycles (defaults to True). sloppy_bound : int, optional The threshold bound used by sloppy (defaults to the number of the beast). """ cycle_count = 0 if reactions is None: reactions = model.reactions else: reactions = model._ids_to_reactions(reactions) fva_sol = OrderedDict() for reaction in reactions: fva_sol[reaction.id] = dict() model.objective = reaction model.objective.direction = 'min' try: solution = model.solve() except Unbounded: fva_sol[reaction.id]['lower_bound'] = -numpy.inf continue except Infeasible: fva_sol[reaction.id]['lower_bound'] = 0 continue bound = solution.f if sloppy and abs(bound) < sloppy_bound: fva_sol[reaction.id]['lower_bound'] = bound else: logger.debug('Determine if {} with bound {} is a cycle'.format( reaction.id, bound)) v0_fluxes = solution.x_dict v1_cycle_free_fluxes = remove_infeasible_cycles(model, v0_fluxes) if abs(v1_cycle_free_fluxes[reaction.id] - bound) < 10**-6: fva_sol[reaction.id]['lower_bound'] = bound else: logger.debug('Cycle detected: {}'.format(reaction.id)) cycle_count += 1 v2_one_cycle_fluxes = remove_infeasible_cycles( model, v0_fluxes, fix=[reaction.id]) with TimeMachine() as tm: for key, v1_flux in six.iteritems(v1_cycle_free_fluxes): if round(v1_flux, config.ndecimals) == 0 and round( v2_one_cycle_fluxes[key], config.ndecimals) != 0: knockout_reaction = model.reactions.get_by_id(key) knockout_reaction.knock_out(time_machine=tm) model.objective.direction = 'min' try: solution = model.solve() except Unbounded: fva_sol[reaction.id]['lower_bound'] = -numpy.inf except Infeasible: fva_sol[reaction.id]['lower_bound'] = 0 else: fva_sol[reaction.id]['lower_bound'] = solution.f for reaction in reactions: model.objective = reaction model.objective.direction = 'max' try: solution = model.solve() except Unbounded: fva_sol[reaction.id]['upper_bound'] = numpy.inf continue except Infeasible: fva_sol[reaction.id]['upper_bound'] = 0 continue bound = solution.f if sloppy and abs(bound) < sloppy_bound: fva_sol[reaction.id]['upper_bound'] = bound else: logger.debug('Determine if {} with bound {} is a cycle'.format( reaction.id, bound)) v0_fluxes = solution.x_dict v1_cycle_free_fluxes = remove_infeasible_cycles(model, v0_fluxes) if abs(v1_cycle_free_fluxes[reaction.id] - bound) < 1e-6: fva_sol[reaction.id]['upper_bound'] = v0_fluxes[reaction.id] else: logger.debug('Cycle detected: {}'.format(reaction.id)) cycle_count += 1 v2_one_cycle_fluxes = remove_infeasible_cycles( model, v0_fluxes, fix=[reaction.id]) with TimeMachine() as tm: for key, v1_flux in six.iteritems(v1_cycle_free_fluxes): if round(v1_flux, config.ndecimals) == 0 and round( v2_one_cycle_fluxes[key], config.ndecimals) != 0: knockout_reaction = model.reactions.get_by_id(key) knockout_reaction.knock_out(time_machine=tm) model.objective.direction = 'max' try: solution = model.solve() except Unbounded: fva_sol[reaction.id]['upper_bound'] = numpy.inf except Infeasible: fva_sol[reaction.id]['upper_bound'] = 0 else: fva_sol[reaction.id]['upper_bound'] = solution.f df = pandas.DataFrame.from_dict(fva_sol, orient='index') lb_higher_ub = df[df.lower_bound > df.upper_bound] # Assert that these cases really only numerical artifacts assert ((lb_higher_ub.lower_bound - lb_higher_ub.upper_bound) < 1e-6).all() df.lower_bound[lb_higher_ub.index] = df.upper_bound[lb_higher_ub.index] return df
def _cycle_free_fva(model, reactions=None, sloppy=True, sloppy_bound=666): """Cycle free flux-variability analysis. (http://cran.r-project.org/web/packages/sybilcycleFreeFlux/index.html) Parameters ---------- model : SolverBasedModel reactions : list List of reactions whose flux-ranges should be determined. sloppy : boolean, optional If true, only fluxes v with abs(v) > sloppy_bound are checked to be futile cycles (defaults to True). sloppy_bound : int, optional The threshold bound used by sloppy (defaults to the number of the beast). """ cycle_count = 0 if reactions is None: reactions = model.reactions else: reactions = model._ids_to_reactions(reactions) fva_sol = OrderedDict() for reaction in reactions: fva_sol[reaction.id] = dict() model.objective = reaction model.objective.direction = 'min' try: solution = model.solve() except Unbounded: fva_sol[reaction.id]['lower_bound'] = -numpy.inf continue except Infeasible: fva_sol[reaction.id]['lower_bound'] = 0 continue bound = solution.f if sloppy and abs(bound) < sloppy_bound: fva_sol[reaction.id]['lower_bound'] = bound else: logger.debug('Determine if {} with bound {} is a cycle'.format(reaction.id, bound)) v0_fluxes = solution.x_dict v1_cycle_free_fluxes = remove_infeasible_cycles(model, v0_fluxes) if abs(v1_cycle_free_fluxes[reaction.id] - bound) < 10 ** -6: fva_sol[reaction.id]['lower_bound'] = bound else: logger.debug('Cycle detected: {}'.format(reaction.id)) cycle_count += 1 v2_one_cycle_fluxes = remove_infeasible_cycles(model, v0_fluxes, fix=[reaction.id]) with TimeMachine() as tm: for key, v1_flux in six.iteritems(v1_cycle_free_fluxes): if round(v1_flux, config.ndecimals) == 0 and round(v2_one_cycle_fluxes[key], config.ndecimals) != 0: knockout_reaction = model.reactions.get_by_id(key) knockout_reaction.knock_out(time_machine=tm) model.objective.direction = 'min' try: solution = model.solve() except Unbounded: fva_sol[reaction.id]['lower_bound'] = -numpy.inf except Infeasible: fva_sol[reaction.id]['lower_bound'] = 0 else: fva_sol[reaction.id]['lower_bound'] = solution.f for reaction in reactions: model.objective = reaction model.objective.direction = 'max' try: solution = model.solve() except Unbounded: fva_sol[reaction.id]['upper_bound'] = numpy.inf continue except Infeasible: fva_sol[reaction.id]['upper_bound'] = 0 continue bound = solution.f if sloppy and abs(bound) < sloppy_bound: fva_sol[reaction.id]['upper_bound'] = bound else: logger.debug('Determine if {} with bound {} is a cycle'.format(reaction.id, bound)) v0_fluxes = solution.x_dict v1_cycle_free_fluxes = remove_infeasible_cycles(model, v0_fluxes) if abs(v1_cycle_free_fluxes[reaction.id] - bound) < 1e-6: fva_sol[reaction.id]['upper_bound'] = v0_fluxes[reaction.id] else: logger.debug('Cycle detected: {}'.format(reaction.id)) cycle_count += 1 v2_one_cycle_fluxes = remove_infeasible_cycles(model, v0_fluxes, fix=[reaction.id]) with TimeMachine() as tm: for key, v1_flux in six.iteritems(v1_cycle_free_fluxes): if round(v1_flux, config.ndecimals) == 0 and round(v2_one_cycle_fluxes[key], config.ndecimals) != 0: knockout_reaction = model.reactions.get_by_id(key) knockout_reaction.knock_out() model.objective.direction = 'max' try: solution = model.solve() except Unbounded: fva_sol[reaction.id]['upper_bound'] = numpy.inf except Infeasible: fva_sol[reaction.id]['upper_bound'] = 0 else: fva_sol[reaction.id]['upper_bound'] = solution.f df = pandas.DataFrame.from_dict(fva_sol, orient='index') lb_higher_ub = df[df.lower_bound > df.upper_bound] # Assert that these cases really only numerical artifacts assert ((lb_higher_ub.lower_bound - lb_higher_ub.upper_bound) < 1e-6).all() df.lower_bound[lb_higher_ub.index] = df.upper_bound[lb_higher_ub.index] return df