def _formulate_imat(model, up_regulated, down_regulated, epsilon = 1): """ Formulates iMAT problem Arguments: model: Model -- a metabolic constraint-based model up_regulated: dict (of str of float): up_regulated reactions with score down_regulated: dict (of str of float): down_regulated reactions with score """ # Constants active_pos = 'active_+' active_neg = 'active_-' inactive = 'inactive_+' # Initialize model solver = get_solver() solver.add_variables(model.get_reactions_bounds()) solver.add_constraints(model.s) # Initialize binary variables for name in model.reactions: solver.add_variable(name + active_pos, var_type=solver.BINARY) solver.add_variable(name + active_neg, var_type=solver.BINARY) if name in down_regulated: solver.add_variable(name + inactive, var_type=solver.BINARY) # Update model wiht binary variables solver.update() # Add binary variables constraints for name in model.reactions: lb, ub = model.get_reaction_bounds(name) # Vi + Yi+ (lb - epsilon) >= lb solver.add_constraint('imat_up_lower_bound_' + name + active_pos, [(name, 1), (name + active_pos, lb), (name + active_pos, -epsilon)], solver.GREATER_EQUAL, lb) # Vi + Yi- (ub + epsilon) <= ub solver.add_constraint('imat_up_upper_bound_' + name + active_neg, [(name, 1), (name + active_neg, ub), (name + active_neg, epsilon)], solver.LESS_EQUAL, ub) if name in down_regulated: # lb (1 - Yi+) <= Vi solver.add_constraint('imat_down_lower_bound_' + name + inactive + '_1', [(name, 1), (name + inactive, lb)], solver.GREATER_EQUAL, lb) # Vi <= ub (1 - Yi+) solver.add_constraint('imat_down_upper_bound_' + name + inactive + '_2', [(name, 1), (name + inactive, ub)], solver.LESS_EQUAL, ub) # Set objective function, max: sum(up_pos + up_neg) + sum(down_pos) objective = [] for name in up_regulated: objective.append((name + active_pos, 1)) objective.append((name + active_neg, 1)) for name in down_regulated: objective.append((name + inactive, 1)) solver.set_objective(objective) return solver
def pFBA(model, objective=None, maximize=True, relax=0.999999): """ Performs a parsimonious flux balance analysis (pFBA) Arguments: model: Model -- a metabolic constraint-based model objective: dict(reaction: value) -- objective coefficients (optional) maximize: bool -- maximize or minimize (maximize by default) Returns: solution: Solution -- simulation solution """ objective = model.get_objective() if objective is None else objective # Initialize model solver = get_solver() solver.add_variables(model.get_reactions_bounds()) solver.add_constraints(model.s) # Perform FBA fba_solution = FBA(model, objective, maximize, solver) # Split reversible reactions for k, (lb, ub) in model.get_reactions_bounds().items(): if lb < 0 < ub: solver.add_variable(k + '_neg', 0, abs(lb)) solver.add_variable(k + '_pos', 0, ub) solver.update() for k, (lb, ub) in model.get_reactions_bounds().items(): if lb < 0 < ub: solver.add_constraint(k + '_neg', {k: 1, k + '_neg': 1}, solver.GREATER_EQUAL, 0) solver.add_constraint(k + '_pos', {k: -1, k + '_pos': 1}, solver.GREATER_EQUAL, 0) # Constraint FBA objective to max value solver.add_constraint('fba_constraint', objective, solver.GREATER_EQUAL, fba_solution.obj_value * relax) # Set pFBA objective: min (internal reactions) pfba_objective = {} for k, (lb, ub) in model.get_reactions_bounds().items(): if lb < 0 < ub: pfba_objective[k + '_neg'] = 1 pfba_objective[k + '_pos'] = 1 else: pfba_objective[k] = 1 solver.set_objective(pfba_objective, False) # Run optimization pfba_solution = solver.optimize() # Merge splited reactions values for k, (lb, ub) in model.get_reactions_bounds().items(): if lb < 0 < ub: del pfba_solution.values[k + '_neg'] del pfba_solution.values[k + '_pos'] return pfba_solution
def FVA(model, reactions=None, objective=None, percentage=None, return_simulations=False, verbose=1): """ Performs a Flux Variability Analysis Arguements: model: Model -- a metabolic constrain-based model reaction: list (of str) -- list of reactions to analyse (optional) objective: dict(reaction: value) -- objective coefficients (optional) percentage: float -- percentage of the objective to keep (optional) Returns: solution: dict (of str of tuple) -- dict with each reaction and the min and max flux value """ # Initialise model solver = get_solver() solver.add_variables(model.get_reactions_bounds()) solver.add_constraints(model.s) # Constraint with defined objective if (objective is not None) and (percentage is not None): solution = FBA(model, objective, True, solver) solver.add_constraint('objective_constraint', objective, solver.EQUAL, solution.obj_value * percentage) # Reactions to analyse reactions = model.reactions.keys() if reactions is None else reactions # Maximize and minimize each reaction at a time fva, solutions = {}, DataFrame(np.NaN, index=reactions, columns={'_'.join([reac, obj]) for reac in reactions for obj in {'min', 'max'}}) for reaction in reactions: # Minimize min_solution = FBA(model, {reaction: 1}, False, solver) min_value = None if min_solution.solution_status == solver.OPTIMAL: min_value = min_solution.obj_value # Maximize max_solution = FBA(model, {reaction: 1}, True, solver) max_value = None if max_solution.solution_status == solver.OPTIMAL: max_value = max_solution.obj_value # Store solution fva[reaction] = [min_value, max_value] if return_simulations: solutions['_'.join([reaction, 'min'])] = Series(min_solution.values.values(), index=min_solution.values.keys())[reactions] solutions['_'.join([reaction, 'max'])] = Series(max_solution.values.values(), index=max_solution.values.keys())[reactions] return (fva, solutions) if return_simulations else fva
def FBA(model, objective=None, maximize=True, solver=None, opt_abs_values=False): """ Performs a Flux Balance Analysis simulation Arguments: model: Model -- a metabolic constraint-based model objective: dict(reaction: value) -- objective coefficients (optional) maximize: bool -- maximize or minimize (maximize by default) solver: Solver -- Solver instance, reuse formulated problem Returns: solution: Solution -- simulation solution """ objective = model.get_objective() if objective is None else objective # Initialize model if solver is None: solver = get_solver() solver.add_variables(model.get_reactions_bounds()) solver.add_constraints(model.s) # Optimisation with absolute objective values if opt_abs_values: # Split reversible reactions for k, (lb, ub) in model.get_reactions_bounds().items(): if lb < 0 < ub: solver.add_variable(k + '_neg', 0, abs(lb)) solver.add_variable(k + '_pos', 0, ub) solver.update() for k, (lb, ub) in model.get_reactions_bounds().items(): if lb < 0 < ub: solver.add_constraint(k + '_neg', {k: 1, k + '_neg': 1}, solver.GREATER_EQUAL, 0) solver.add_constraint(k + '_pos', {k: -1, k + '_pos': 1}, solver.GREATER_EQUAL, 0) # Set objective: min/max: abs(objective) abs_objective = {} for k, (lb, ub) in model.get_reactions_bounds(objective.keys()).items(): if lb < 0 < ub: abs_objective[k + '_neg'] = 1 abs_objective[k + '_pos'] = 1 else: abs_objective[k] = 1 objective = abs_objective.copy() # Set objective function solver.set_objective(objective, maximize) # Run optimization solution = solver.optimize() # If optimisation with absolute objective values if opt_abs_values: # Merge splited reactions values for k, (lb, ub) in model.get_reactions_bounds().items(): if lb < 0 < ub: del solution.values[k + '_neg'] del solution.values[k + '_pos'] return solution
def min_differences(model, reference=None, multipliers={}, mode=''): """ Minimizes the absolute difference between the model simulation and the given flux values. Arguments: model: Model -- a metabolic constraint-based model flux_values: dict (of str and float): reference flux values multipliers: dict (of str and float): reactions objective multipliers mode: ['', 'backward', 'forward']: minimise a given direction of the reversible reactions """ # Run pFBA if reference is missing if reference is None: reference = pFBA(model).values # Initialize model solver = get_solver() solver.add_variables(model.get_reactions_bounds()) solver.add_constraints(model.s) # Split reversible reactions for reaction, (lb, ub) in model.get_reactions_bounds().items(): if reaction in reference and lb < 0 < ub: solver.add_variable(reaction + '_backward', 0, abs(lb)) solver.add_variable(reaction + '_forward', 0, ub) # Create objective variables objective = [] for reaction in reference: # Get reaction boundaries and multiplier lb, ub = model.get_reaction_bounds(reaction) r_mul = multipliers[reaction] if reaction in multipliers else 1 # Create positive and negative variables for reversivle and non-reversible reactions reaction_vars = {reaction + s1 + s2 if lb < 0 < ub else reaction + s1 for s1 in ['_f_neg', '_f_pos'] for s2 in ['_backward', '_forward']} solver.add_variables_from_list(reaction_vars, lower_bound=0) # Add variables to objective function according to chosen mode objective.extend([(r, r_mul) for r in reaction_vars if r.endswith(mode)]) # UPDATE variables then start adding constraints solver.update() # Add positive range constrains of splitted reactions for reaction, (lb, ub) in model.get_reactions_bounds().items(): if reaction in reference and lb < 0 < ub: solver.add_constraint(reaction + '_backward', {reaction: 1, reaction + '_backward': 1}, solver.GREATER_EQUAL, 0) solver.add_constraint(reaction + '_forward', {reaction: -1, reaction + '_forward': 1}, solver.GREATER_EQUAL, 0) # Set difference constraints: F1 - V > -Vref; F2 + V > Vref for reaction, value in reference.items(): # Get reaction boundaries lb, ub = model.get_reaction_bounds(reaction) # Create positive and negative variables for reversivle and non-reversible reactions if lb < 0 < ub: for s2 in ['_backward', '_forward']: solver.add_constraint(reaction + '_f_pos' + s2 + '_c', {reaction + '_f_pos' + s2: 1, reaction: -1}, solver.GREATER_EQUAL, -value) solver.add_constraint(reaction + '_f_neg' + s2 + '_c', {reaction + '_f_neg' + s2: 1, reaction: 1}, solver.GREATER_EQUAL, value) else: solver.add_constraint(reaction + '_f_pos' + '_c', {reaction + '_f_pos': 1, reaction: -1}, solver.GREATER_EQUAL, -value) solver.add_constraint(reaction + '_f_neg' + '_c', {reaction + '_f_neg': 1, reaction: 1}, solver.GREATER_EQUAL, value) # Set objective variables: min: F1 + F2 solver.set_objective(objective, False) # Run optimization solution = solver.optimize() return solution