def allot_consistent_stoichiometry(graph, factors):
    """
    @param graph: L{BipartiteMetabolicNetwork
        <pyMetabolism.graph_view.BipartiteMetabolicNetwork>}

    @param factors: An iterable of desired stoichiometric factors. May contain
        a specific factor multiple times to bias towards selection of that factor.

    @rtype: L{BipartiteMetabolicNetwork
        <pyMetabolism.graph_view.BipartiteMetabolicNetwork>} with consistent
        stoichiometric factors.
    """
    options = OptionsManager()
    logger = logging.getLogger("%s.allot_consistent_stoichiometry"\
        % options.main_logger_name)
    def populate_network(graph, factors):
        for e in graph.edges_iter():
            graph[e[0]][e[1]]["factor"] = random.choice(factors)
    count = 0
    while (True):
        count += 1
        logger.info("Attempt number %d", count)
        populate_network(graph, factors)
        matrix = StoichiometricMatrix()
        matrix.make_new_from_network(graph)
        (yes, result) = verify_consistency(matrix)
        if yes:
            logger.info("Success!")
            break
def balance_multiple_reactions(graph, factors, rxns, mass_vector, lower, upper, logger):
    options = OptionsManager()
    sub = BipartiteMetabolicNetwork()
    for rxn in rxns:
        msg = "%s:\n" % rxn.identifier
        sub.add_reaction(rxn)
        for cmpd in graph.predecessors(rxn):
            coefficient = random.choice(factors)
            msg += " -%d%s" % (coefficient, cmpd)
            sub.add_compound(cmpd)
            sub.add_edge(cmpd, rxn, factor=coefficient)
            graph[cmpd][rxn]["factor"] = coefficient
        for cmpd in graph.successors(rxn):
            coefficient = random.choice(factors)
            msg += " +%d%s" % (coefficient, cmpd)
            sub.add_compound(cmpd)
            sub.add_edge(rxn, cmpd, factor=coefficient)
            graph[rxn][cmpd]["factor"] = coefficient
        msg += " = 0\n"
        logger.debug(msg)
    matrix = StoichiometricMatrix()
    matrix.make_new_from_network(sub)
    # objective function
    objective = numpy.ones(matrix.num_compounds)
    # equality constraints
    A_eq = numpy.array(numpy.transpose(matrix.matrix), dtype=float, copy=False)
    b_eq = numpy.zeros(matrix.num_reactions)
    # lower boundary
    lb = numpy.empty(matrix.num_compounds)
    for (i, comp) in enumerate(matrix.compounds):
        if mass_vector[comp] != 0.:
            lb[i] = mass_vector[comp]
        else:
            lb[i] = lower
    # upper boundary
    ub = numpy.empty(matrix.num_compounds)
    for (i, comp) in enumerate(matrix.compounds):
        if mass_vector[comp] != 0.:
            ub[i] = mass_vector[comp]
        else:
            ub[i] = upper
    # starting guess
    start = numpy.empty(matrix.num_compounds)
    logger.debug(A_eq)
    for (i, comp) in enumerate(matrix.compounds):
        start[i] = 1. / float(sub.in_degree(comp) + sub.out_degree(comp))
    problem = openopt.LP(f=objective, Aeq=A_eq, beq=b_eq, lb=lb, ub=ub, x0=start)
    result = problem.solve(options.solver, iprint=-10)
    if not result.isFeasible:
        raise PyMetabolismError("Unable to balance reaction system.")
    for (i, comp) in enumerate(matrix.compounds):
        if mass_vector[comp] == 0.:
            mass_vector[comp] = result.xf[i]
    logger.debug(result.xf)
def random_fba(graph, num_inputs, num_outputs):
    """
    @param graph: BipartiteMetabolicNetwork

    @param num_inputs: Sequence of integers for a number of compound sources on
        the same set of sink compounds

    @param num_outputs: Size of the sink vector
    """
    options = OptionsManager()
    logger = logging.getLogger("%s.random_fba" % options.main_logger_name)
    assert num_outputs > 0
    if not num_inputs:
        raise PyMetabolismError("No sources specified for random FBA!")
    for input in num_inputs:
        assert input > 0
    inputs = list()
    potential_inputs = list()
    outputs = list()
    results = list()
    cmpds = list(graph.compounds)
    for comp in graph.compounds:
        if len(outputs) < num_outputs and graph.out_degree(comp) == 0:
            outputs.append(comp)
            cmpds.remove(comp)
        elif graph.in_degree(comp) == 0:
            potential_inputs.append(comp)
            cmpds.remove(comp)

    # if necessary fill up 'outputs' with random compounds
    # they should not be source compounds, thus we remove references
    count = num_outputs - len(outputs)
    while (count > 0):
        comp = random.choice(cmpds)
        outputs.append(comp)
        cmpds.remove(comp)
        count -= 1
    logger.info("%d biomass compounds.", len(outputs))

    # perform FBA for varying numbers of source compounds
    matrix = StoichiometricMatrix()
    matrix.make_new_from_network(graph)
    
    # add biomass reaction
    biomass = Reaction("Biomass", tuple(outputs), (), [-1.]*num_outputs)
    
    # equality constraints
    b_eq = numpy.zeros(matrix.num_compounds)

    # start with the largest number of inputs, then reduce
    num_inputs.sort(reverse=True)
    maxi = num_inputs[0]
    for comp in potential_inputs:
            if len(inputs) < maxi:
                inputs.append(comp)
    # similarly to 'outputs', 'inputs' is filled up
    count = maxi - len(inputs)
    while (count > 0):
        comp = random.choice(cmpds)
        inputs.append(comp)
        cmpds.remove(comp)
        count -= 1
    
    for num in num_inputs:
        system = numpy.array(matrix, dtype=float, copy=True)
        system.add_reaction(biomass)
        logger.info("%d uptake compounds.", num)
        input_rxns = list()
        # adjust source numbers
        while len(inputs) > num:
            comp = random.choice(inputs)
            inputs.remove(comp)

        transp = list()
        for comp in inputs:
            rxn = Reaction("Transport_%s" % comp.identifier,\
                (), (comp), (1.), reversible=True)
            system.add_reaction(rxn)
            transp.append(rxn)

        # objective function
        objective = numpy.zeros(system.num_reactions)
        objective[system.reaction_map[biomass]] = 1.

        # equality constraints
        A_eq = system

        # prepare flux balance analysis linear programming problem
        
        # boundaries
        lb = numpy.zeros(system.num_reactions)
        for rxn in system.reactions:
            if rxn.reversible:
                lb[system.reaction_map[rxn]] = -numpy.inf
        ub = numpy.empty(system.num_reactions)
        ub.fill(numpy.inf)
        # constrain transport reactions
        for rxn in transp:
            ub[system.reaction_map[rxn]] = 1.
            lb[system.reaction_map[rxn]] = -1.
                    
        problem = openopt.LP(f=objective, Aeq=A_eq, beq=b_eq, lb=lb, ub=ub)
        result = problem.maximize(options.solver, iprint=-10)
        logger.debug(result.xf)
        for rxn in graph.reactions:
            if rxn == biomass:
                logger.info("%s : %G", rxn.identifier, result.xf[system.reaction_map[rxn]])
            elif rxn in transp:
                logger.info("%s : %G", rxn.identifier, result.xf[system.reaction_map[rxn]])
            else:
                logger.info("%s : %G", rxn.identifier, result.xf[system.reaction_map[rxn]])
        results.append((result.isFeasible, result.xf))
    return results