示例#1
0
def moma(wt_model, mutant_model, objective_sense='maximize', solver=None,
         tolerance_optimality=1e-8, tolerance_feasibility=1e-8,
         minimize_norm=False, the_problem='return', lp_method=0,
         combined_model=None, norm_type='euclidean'):
    """Runs the minimization of metabolic adjustment method described in
    Segre et al 2002 PNAS 99(23): 15112-7.

    wt_model: A cobra.Model object

    mutant_model: A cobra.Model object with different reaction bounds vs wt_model.
    To simulate deletions

    objective_sense: 'maximize' or 'minimize'

    solver: 'gurobi', 'cplex', or 'glpk'.  Note: glpk cannot be used with
    norm_type 'euclidean'

    tolerance_optimality: Solver tolerance for optimality.

    tolerance_feasibility: Solver tolerance for feasibility.

    the_problem: None or a problem object for the specific solver that can be
    used to hot start the next solution.

    lp_method: The method to use for solving the problem.  Depends on the solver.  See
    the cobra.flux_analysis.solvers.py file for more info.
        For norm_type == 'euclidean':
            the primal simplex works best for the test model (gurobi: lp_method=0, cplex: lp_method=1)
    
    combined_model: an output from moma that represents the combined optimization
    to be solved.  when this is not none.  only assume that bounds have changed
    for the mutant or wild-type.  This saves 0.2 seconds in stacking matrices.


    NOTE: Current function makes too many assumptions about the structures of the models


    """
    if solver is None:
        if norm_type == "euclidiean":
            solver = get_solver_name(qp=True)
        else:
            solver = get_solver_name()  # linear is not even implemented yet
    if combined_model is not None or the_problem not in ['return']:
        warn("moma currently does not support reusing models or problems. " +\
             "continuing without them")
        combined_model = None
        the_problem = 'return'
    if solver.lower() == 'cplex' and lp_method == 0:
        #print 'for moma, solver method 0 is very slow for cplex. changing to method 1'
        lp_method = 1
    if solver.lower() == 'glpk' and norm_type == 'euclidean':
        try:
            from gurobipy import Model
            solver = 'gurobi'
            warn("GLPK can't solve quadratic problems like MOMA.  Switched solver to %s"%solver)
        except:
            warn("GLPK can't solve quadratic problems like MOMA.  Switching to linear MOMA")

    if norm_type == 'euclidean':
        #Reusing the basis can get the solver stuck.
        reuse_basis = False
    if combined_model and combined_model.norm_type != norm_type:
        print('Cannot use combined_model.norm_type = %s with user-specified norm type'%(combined_model.norm_type,
                                                                                        norm_type))
        print('Defaulting to user-specified norm_type')
        combined_model = None



    number_of_reactions_in_common = len(set([x.id for x in wt_model.reactions]).intersection([x.id for x in mutant_model.reactions]))
    number_of_reactions = len(wt_model.reactions) + len(mutant_model.reactions)

    #Get the optimal wt objective value and adjust based on optimality tolerances
    wt_model.optimize(solver=solver)
    wt_optimal = deepcopy(wt_model.solution.f)
    if objective_sense == 'maximize':
        wt_optimal = floor(wt_optimal/tolerance_optimality)*tolerance_optimality
    else:
        wt_optimal = ceil(wt_optimal/tolerance_optimality)*tolerance_optimality
    if norm_type == 'linear':
        raise Exception('linear MOMA is not currently implmented')
        quadratic_component = None
    if minimize_norm:
        raise Exception('minimize_norm is not currently implemented')
        #just worry about the flux distribution and not the objective from the wt
        combined_model = mutant_model.copy()
        #implement this: combined_model.reactions[:].objective_coefficients = -wt_solution.x_dict
    else:
        #Construct a problem that attempts to maximize the objective in the WT model while
        #solving the quadratic problem.  This new problem is constructed to try to find
        #a solution for the WT model that lies close to the mutant model.  There are
        #often multiple equivalent solutions with M matrices and the one returned
        #by a simple cobra_model.optimize call may be too far from the mutant.
        #This only needs to be adjusted if we update mutant_model._S after deleting reactions
        if not combined_model:
            #Collect the set of wt reactions contributing to the objective.
            objective_reaction_coefficient_dict = dict([(x.id, x.objective_coefficient)
                                                        for x in wt_model.reactions
                                                        if x.objective_coefficient])
            
            
            combined_model = construct_difference_model(wt_model, mutant_model, norm_type)
            #Add in the virtual objective metabolite to constrain the wt_model to the space where
            #the objective was maximal
            objective_metabolite = Metabolite('wt_optimal')
            objective_metabolite._bound = wt_optimal
            if objective_sense == 'maximize':
                objective_metabolite._constraint_sense = 'G'
            else:
                objective_metabolite._constraint_sense = 'L'

            #TODO: this couples the wt_model objective reaction to the virtual metabolite
            #Currently, assumes a single objective reaction; however, this may be extended
            [combined_model.reactions.get_by_id(k).add_metabolites({objective_metabolite: v})
             for k, v in objective_reaction_coefficient_dict.items()]
                
            
            



        if norm_type == 'euclidean':
            #Makes assumptions about the structure of combined model
            quadratic_component = s_vstack((lil_matrix((number_of_reactions, number_of_reactions + number_of_reactions_in_common )),
                                            s_hstack((lil_matrix((number_of_reactions_in_common, number_of_reactions)),
                                                      eye(number_of_reactions_in_common,number_of_reactions_in_common)))))
    
        elif norm_type == 'linear':
            quadratic_component = None

    combined_model.norm_type = norm_type
    cobra_model = combined_model

    the_problem = combined_model.optimize(objective_sense='minimize',
                                         quadratic_component=quadratic_component,
                                         solver=solver,
                                         tolerance_optimality=tolerance_optimality,
                                         tolerance_feasibility=tolerance_feasibility,
                                         lp_method=lp_method, reuse_basis=reuse_basis)

    if combined_model.solution.status != 'optimal':
        warn('optimal moma solution not found: solver status %s'%combined_model.solution.status +\
             ' returning the problem, the_combined model, and the quadratic component for trouble shooting')
        return(the_problem, combined_model, quadratic_component)
             
    solution = combined_model.solution
    mutant_dict = {}
    #Might be faster to quey based on mutant_model.reactions with the 'mutant_' prefix added
    _reaction_list = [x for x in combined_model.reactions if x.id.startswith('mutant_')]
    mutant_f = sum([mutant_model.reactions.get_by_id(x.id[len('mutant_'):]).objective_coefficient *
                    x.x for x in _reaction_list])
    mutant_dict['objective_value'] = mutant_f
    wild_type_flux_total = sum([abs(solution.x_dict[x.id]) for x in wt_model.reactions])
    mutant_flux_total = sum(abs(x.x) for x in _reaction_list)
    #Need to use the new solution as there are multiple ways to achieve an optimal solution in
    #simulations with M matrices.
    mutant_dict['status'] = solution.status
    #TODO: Deal with maximize / minimize issues for a reversible model that's been converted to irreversible
    mutant_dict['flux_difference'] = flux_difference = sum([(solution.x_dict[x.id[len('mutant_'):]]
                                                             - x.x)**2 for x in _reaction_list])
    mutant_dict['the_problem'] = the_problem
    mutant_dict['combined_model'] = combined_model
    
    del wt_model, mutant_model, quadratic_component, solution
    return(mutant_dict)
示例#2
0
文件: moma.py 项目: sandrejev/bioopt
def moma(wt_model, mutant_model, objective_sense='maximize', solver=None,
         tolerance_optimality=1e-8, tolerance_feasibility=1e-8,
         minimize_norm=True, norm_flux_dict=None, the_problem='return', lp_method=0,
         combined_model=None, norm_type='euclidean'):
    """Runs the minimization of metabolic adjustment method described in
    Segre et al 2002 PNAS 99(23): 15112-7.

    wt_model: A cobra.Model object
    mutant_model: A cobra.Model object with different reaction bounds vs wt_model.
    objective_sense: 'maximize' or 'minimize'
    solver: 'gurobi', 'cplex', or 'glpk'.  Note: glpk cannot be used with norm_type 'euclidean'
    tolerance_optimality: Solver tolerance for optimality.
    tolerance_feasibility: Solver tolerance for feasibility.
    the_problem: None or a problem object for the specific solver that can be
    used to hot start the next solution.
    lp_method: The method to use for solving the problem.  Depends on the solver.  See
    the cobra.flux_analysis.solvers.py file for more info.
        For norm_type == 'euclidean':
            the primal simplex works best for the test model (gurobi: lp_method=0, cplex: lp_method=1)
    combined_model: an output from moma that represents the combined optimization to be solved.

    """
    if solver is None:
        if norm_type == "euclidean":
            solver = get_solver_name(qp=True)
        else:
            solver = get_solver_name()  # linear is not even implemented yet
    if combined_model is not None or the_problem not in ['return']:
        warn("moma currently does not support reusing models or problems. " +\
             "continuing without them")
        combined_model = None
        the_problem = 'return'
    if solver.lower() == 'cplex' and lp_method == 0:
        #print 'for moma, solver method 0 is very slow for cplex. changing to method 1'
        lp_method = 1
    if solver.lower() == 'glpk' and norm_type == 'euclidean':
        try:
            # from gurobipy import Model
            solver = 'gurobi'
            warn("GLPK can't solve quadratic problems like MOMA.  Switched solver to %s"%solver)
        except:
            warn("GLPK can't solve quadratic problems like MOMA.  Switching to linear MOMA")

    if norm_type == 'euclidean':
        #Reusing the basis can get the solver stuck.
        reuse_basis = False
    if combined_model and combined_model.norm_type != norm_type:
        print('Cannot use combined_model.norm_type = %s with user-specified norm type'%(combined_model.norm_type,
                                                                                        norm_type))
        print('Defaulting to user-specified norm_type')
        combined_model = None


    if norm_type == 'linear':
        raise Exception('linear MOMA is not currently implmented')
        quadratic_component = None

    if minimize_norm:
        if norm_flux_dict is None:
            optimize_minimum_flux(wt_model, objective_sense='maximize',
                                  tolerance_feasibility=tolerance_feasibility)
            norm_flux_dict = wt_model.solution.x_dict

        else:
            # update the solution of wt model according to norm_flux_dict
            wt_model.optimize() # this is just to make sure wt_model.solution and mutant_model.solution refer to different object.
            objective_reaction_coefficient_dict = dict([(x.id, x.objective_coefficient)
                                                        for x in wt_model.reactions
                                                        if x.objective_coefficient])
            try:
                wt_model.solution.f = sum([norm_flux_dict[k] * v for k, v in
                                           objective_reaction_coefficient_dict.items()])
                wt_model.solution.x_dict = norm_flux_dict
            except:
                print('incorrect norm_flux_dict')
                raise

        # formulate qMOMA using wt_flux as reference
        # make a copy not to change the objective coefficients of original mutant model
        mutant_model_moma = mutant_model.copy()
        nRxns = len(mutant_model_moma.reactions)
        quadratic_component = 2 * eye(nRxns, nRxns)
        # linear component
        [setattr(x, 'objective_coefficient', -2 * norm_flux_dict[x.id]) for x in mutant_model_moma.reactions]

        the_problem = mutant_model_moma.optimize(objective_sense='minimize',
                                         quadratic_component=quadratic_component,
                                         solver=solver,
                                         tolerance_optimality=tolerance_optimality,
                                         tolerance_feasibility=tolerance_feasibility,
                                         lp_method=lp_method)
                                         #, reuse_basis=reuse_basis) # this should be commented out when solver is 'cplex'

        if mutant_model_moma.solution.status != 'optimal':
            warn('optimal moma solution not found: solver status %s'%mutant_model_moma.solution.status +\
                 ' returning the problem, the_combined model, and the quadratic component for trouble shooting')
            return(the_problem, mutant_model_moma, quadratic_component)

        solution = mutant_model_moma.solution
        mutant_dict = {}
        mutant_f = sum([mutant_model.reactions.get_by_id(x.id).objective_coefficient * x.x for x in mutant_model_moma.reactions])
        mutant_dict['objective_value'] = mutant_f
        mutant_dict['status'] = solution.status
        #TODO: Deal with maximize / minimize issues for a reversible model that's been converted to irreversible
        mutant_dict['flux_difference'] = flux_difference = sum([(norm_flux_dict[r.id] - mutant_model_moma.solution.x_dict[r.id])**2
                                                                for r in mutant_model_moma.reactions])
        mutant_dict['the_problem'] = the_problem
        mutant_dict['mutant_model'] = mutant_model_moma

        # update the solution of original mutant model
        mutant_model.solution.x_dict = the_problem.x_dict
        mutant_model.solution.status = solution.status
        mutant_model.solution.f = mutant_f

        # del wt_model, mutant_model, quadratic_component, solution
        return(mutant_dict)


    else:
        #Construct a problem that attempts to maximize the objective in the WT model while
        #solving the quadratic problem.  This new problem is constructed to try to find
        #a solution for the WT model that lies close to the mutant model.  There are
        #often multiple equivalent solutions with M matrices and the one returned
        #by a simple cobra_model.optimize call may be too far from the mutant.
        #This only needs to be adjusted if we update mutant_model._S after deleting reactions
        number_of_reactions_in_common = len(set([x.id for x in wt_model.reactions]).intersection([x.id for x in mutant_model.reactions]))
        number_of_reactions = len(wt_model.reactions) + len(mutant_model.reactions)
        #Get the optimal wt objective value and adjust based on optimality tolerances

        wt_model.optimize(solver=solver)
        wt_optimal = deepcopy(wt_model.solution.f)
        if objective_sense == 'maximize':
            wt_optimal = floor(wt_optimal/tolerance_optimality)*tolerance_optimality
        else:
            wt_optimal = ceil(wt_optimal/tolerance_optimality)*tolerance_optimality

        if not combined_model:
            #Collect the set of wt reactions contributing to the objective.
            objective_reaction_coefficient_dict = dict([(x.id, x.objective_coefficient)
                                                        for x in wt_model.reactions
                                                        if x.objective_coefficient])
            
            
            combined_model = construct_difference_model(wt_model, mutant_model, norm_type)
            #Add in the virtual objective metabolite to constrain the wt_model to the space where
            #the objective was maximal
            objective_metabolite = Metabolite('wt_optimal')
            objective_metabolite._bound = wt_optimal
            if objective_sense == 'maximize':
                objective_metabolite._constraint_sense = 'G'
            else:
                objective_metabolite._constraint_sense = 'L'

            #TODO: this couples the wt_model objective reaction to the virtual metabolite
            #Currently, assumes a single objective reaction; however, this may be extended
            [combined_model.reactions.get_by_id(k).add_metabolites({objective_metabolite: v})
             for k, v in objective_reaction_coefficient_dict.items()]
                

        if norm_type == 'euclidean':
            #Makes assumptions about the structure of combined model
            quadratic_component = s_vstack((lil_matrix((number_of_reactions, number_of_reactions + number_of_reactions_in_common )),
                                            s_hstack((lil_matrix((number_of_reactions_in_common, number_of_reactions)),
                                                      eye(number_of_reactions_in_common,number_of_reactions_in_common)))))
    
        elif norm_type == 'linear':
            quadratic_component = None

    combined_model.norm_type = norm_type
    cobra_model = combined_model

    the_problem = combined_model.optimize(objective_sense='minimize',
                                         quadratic_component=quadratic_component,
                                         solver=solver,
                                         tolerance_optimality=tolerance_optimality,
                                         tolerance_feasibility=tolerance_feasibility,
                                         lp_method=lp_method) #, reuse_basis=reuse_basis) # this should be commented out when solver is 'cplex'

    if combined_model.solution.status != 'optimal':
        warn('optimal moma solution not found: solver status %s'%combined_model.solution.status +\
             ' returning the problem, the_combined model, and the quadratic component for trouble shooting')
        return(the_problem, combined_model, quadratic_component)
             
    solution = combined_model.solution
    mutant_dict = {}
    #Might be faster to quey based on mutant_model.reactions with the 'mutant_' prefix added
    _reaction_list = [x for x in combined_model.reactions if x.id.startswith('mutant_')]
    mutant_f = sum([mutant_model.reactions.get_by_id(x.id[len('mutant_'):]).objective_coefficient *
                    x.x for x in _reaction_list])
    mutant_dict['objective_value'] = mutant_f
    wild_type_flux_total = sum([abs(solution.x_dict[x.id]) for x in wt_model.reactions])
    mutant_flux_total = sum(abs(x.x) for x in _reaction_list)
    #Need to use the new solution as there are multiple ways to achieve an optimal solution in
    #simulations with M matrices.
    mutant_dict['status'] = solution.status
    #TODO: Deal with maximize / minimize issues for a reversible model that's been converted to irreversible
    mutant_dict['flux_difference'] = flux_difference = sum([(solution.x_dict[x.id[len('mutant_'):]]
                                                             - x.x)**2 for x in _reaction_list])
    mutant_dict['the_problem'] = the_problem
    mutant_dict['combined_model'] = combined_model
    
    del wt_model, mutant_model, quadratic_component, solution
    return(mutant_dict)
示例#3
0
文件: moma.py 项目: fmitha/cobrapy
def moma(wt_model, mutant_model, objective_sense='maximize', solver='gurobi',
         tolerance_optimality=1e-8, tolerance_feasibility=1e-8,
         minimize_norm=False, the_problem='return', lp_method=0,
         combined_model=None, norm_type='euclidean', print_time=False):
    """Runs the minimization of metabolic adjustment method described in
    Segre et al 2002 PNAS 99(23): 15112-7.

    wt_model: A cobra.Model object

    mutant_model: A cobra.Model object with different reaction bounds vs wt_model.
    To simulate deletions

    objective_sense: 'maximize' or 'minimize'

    solver: 'gurobi', 'cplex', or 'glpk'.  Note: glpk cannot be used with
    norm_type 'euclidean'

    tolerance_optimality: Solver tolerance for optimality.

    tolerance_feasibility: Solver tolerance for feasibility.

    the_problem: None or a problem object for the specific solver that can be
    used to hot start the next solution.

    lp_method: The method to use for solving the problem.  Depends on the solver.  See
    the cobra.flux_analysis.solvers.py file for more info.
        For norm_type == 'euclidean':
            the primal simplex works best for the test model (gurobi: lp_method=0, cplex: lp_method=1)
    
    combined_model: an output from moma that represents the combined optimization
    to be solved.  when this is not none.  only assume that bounds have changed
    for the mutant or wild-type.  This saves 0.2 seconds in stacking matrices.

    """
    warn('MOMA is currently non-functional.  check back later')
    if solver.lower() == 'cplex' and lp_method == 0:
        #print 'for moma, solver method 0 is very slow for cplex. changing to method 1'
        lp_method = 1
    if solver.lower() == 'glpk' and norm_type == 'euclidean':
        print "GLPK can't solve quadratic problems like MOMA.  Switching to linear MOMA"

    if norm_type == 'euclidean':
        #Reusing the basis can get the solver stuck.
        reuse_basis = False
    if combined_model and combined_model.norm_type != norm_type:
        print 'Cannot use combined_model.norm_type = %s with user-specified norm type'%(combined_model.norm_type,
                                                                                        norm_type)
        print 'Defaulting to user-specified norm_type'
        combined_model = None
    #Add a prefix in front of the mutant_model metabolites and reactions to prevent
    #name collisions in DictList
    for the_dict_list in [mutant_model.metabolites,
                          mutant_model.reactions]:
        [setattr(x, 'id', 'mutant_%s'%x.id)
         for x in the_dict_list]
        the_dict_list._generate_index() #Update the DictList.dicts


    wt_model.optimize(solver=solver)
    wt_solution = deepcopy(wt_model.solution)
    if objective_sense == 'maximize':
        wt_optimal = floor(wt_solution.f/tolerance_optimality)*tolerance_optimality
    else:
        wt_optimal = ceil(wt_solution.f/tolerance_optimality)*tolerance_optimality
    if norm_type == 'euclidean':
        quadratic_component = eye(wt_solution.x.shape[0],wt_solution.x.shape[0])
    elif norm_type == 'linear':
        raise Exception('linear MOMA is not currently implmented')
        quadratic_component = None
    if minimize_norm:
        raise Exception('minimize_norm is not currently implemented')
        #just worry about the flux distribution and not the objective from the wt
        combined_model = mutant_model.copy()
        #implement this: combined_model.reactions[:].objective_coefficients = -wt_solution.x_dict
    else:
        #Construct a problem that attempts to maximize the objective in the WT model while
        #solving the quadratic problem.  This new problem is constructed to try to find
        #a solution for the WT model that lies close to the mutant model.  There are
        #often multiple equivalent solutions with M matrices and the one returned
        #by a simple cobra_model.optimize call may be too far from the mutant.
        #This only needs to be adjusted if we update mutant_model._S after deleting reactions

        if print_time:
            start_time = time()
        number_of_reactions = len(mutant_model.reactions)
        if norm_type == 'euclidean':
            reaction_coefficient = 1
        elif norm_type == 'linear':
            reaction_coefficient = 2
        if not combined_model:
            #Collect the set of wt reactions contributing to the objective.
            objective_reaction_coefficient_dict = dict([(x.id, x.objective_coefficient)
                                                        for x in wt_model.reactions
                                                        if x.objective_coefficient])
            #This does a deepcopy of both models which might result in a huge overhead.
            #Update cobra.core.Model to improve performance.
            combined_model = wt_model + mutant_model
            if print_time:
                print 'add time %f'%(time()-start_time)
            [setattr(x, 'objective_coefficient', 0.)
             for x in combined_model.reactions]
            #Add in the difference reactions.  The mutant reactions and metabolites are already added.
            #This must be a list to maintain the correct order when adding the difference_metabolites

            difference_reactions = [Reaction('difference_%i'%i)
                                        for i in range(reaction_coefficient*number_of_reactions)]
            [setattr(x, 'lower_bound', -1000)
             for x in difference_reactions]
            combined_model.add_reactions(difference_reactions)
            index_to_reaction = combined_model.reactions
            id_to_reaction = combined_model.reactions._object_dict
            #This is slow
            #Add in difference metabolites
            difference_metabolite_dict = dict([(i, Metabolite('difference_%i'%i))
                                           for i in xrange(number_of_reactions)])
            combined_model.add_metabolites(difference_metabolite_dict.values())
            for i, tmp_metabolite in difference_metabolite_dict.iteritems():
                if norm_type == 'linear':
                    tmp_metabolite._constraint_sense = 'G'
                index_to_reaction[i].add_metabolites({tmp_metabolite: -1.},
                                                     add_to_container_model=False)
                index_to_reaction[i+number_of_reactions].add_metabolites({tmp_metabolite: 1.}, add_to_container_model=False)
                index_to_reaction[i+2*number_of_reactions].add_metabolites({tmp_metabolite: 1.}, add_to_container_model=False)

            #Add in the virtual objective metabolite
            objective_metabolite = Metabolite('wt_optimal')
            objective_metabolite._bound = wt_optimal
            if objective_sense == 'maximize':
                objective_metabolite._constraint_sense = 'G'
            else:
                objective_metabolite._constraint_sense = 'L'
            #TODO: this couples the wt_model objective reaction to the virtual metabolite
            #Currently, assumes a single objective reaction; however, this may be extended
            [id_to_reaction[k].add_metabolites({objective_metabolite: v})
             for k, v in objective_reaction_coefficient_dict.items()]

            if print_time:
                print 'Took %f seconds to construct combined model'%(time()-start_time)
                start_time = time()



        if norm_type == 'euclidean':
            quadratic_component = s_vstack((lil_matrix((2*number_of_reactions, 3*number_of_reactions)),
                                            s_hstack((lil_matrix((number_of_reactions, 2*number_of_reactions)),
                                                      quadratic_component))))
        elif norm_type == 'linear':
            quadratic_component = None

    combined_model.norm_type = norm_type
    cobra_model = combined_model

    if print_time:
        print 'Took %f seconds to update combined model'%(time()-start_time)
        start_time = time()
    the_result = combined_model.optimize(objective_sense='minimize',
                                         quadratic_component=quadratic_component,
                                         solver=solver,
                                         tolerance_optimality=tolerance_optimality,
                                         tolerance_feasibility=tolerance_feasibility,
                                         lp_method=lp_method, reuse_basis=reuse_basis)
    the_problem = the_result
    the_solution = combined_model.solution

    if print_time:
        print 'Took %f seconds to solve problem'%(time()-start_time)
        start_time = time()
    mutant_dict = {}
    x_vector = the_solution.x
    if hasattr(x_vector, 'flatten'):
        x_vector = x_vector.flatten()
    mutant_dict['x'] = mutant_fluxes = array(x_vector[1*number_of_reactions:2*number_of_reactions])
    #Need to use the new solution as there are multiple ways to achieve an optimal solution in
    #simulations with M matrices.
    wt_model.solution.x = array(x_vector[:number_of_reactions])
    mutant_dict['objective_value'] = mutant_f = float(matrix(mutant_fluxes)*matrix([x.objective_coefficient
                                                                                    for x in mutant_model.reactions]).T)
    mutant_dict['status'] = the_solution.status
    mutant_dict['flux_difference'] = flux_difference = sum((wt_model.solution.x - mutant_fluxes)**2)
    mutant_dict['the_problem'] = the_problem
    mutant_dict['combined_model'] = combined_model
    if print_time:
        print 'Took %f seconds to assemble solution'%(time()-start_time)
    
    del wt_model, mutant_model, quadratic_component, the_solution
    return(mutant_dict)