def random_metabolic_network(compounds, reactions, reversible, p, seed=None):
    """
    Creates a bipartite graph that models metabolism according to the principles
    of an Erdos-Renyi-like random graph.

    @param compounds: Integer specifying the number of compounds.

    @param reactions: Integer, specifying the number of reactions.

    @param p: Probability for an edge to be set (on average this is the density
        of the graph).

    @rtype: L{BipartiteMetabolicNetwork
        <pyMetabolism.graph_view.BipartiteMetabolicNetwork>}

    @raise ValueError: If either compounds or reactions are not integers.
    """
    if seed:
        random.seed(seed)
    options = OptionsManager()
    logger = logging.getLogger("%s.random_metabolic_network"\
        % (options.main_logger_name))
    graph = BipartiteMetabolicNetwork(name="random model")
    for i in xrange(compounds):
        graph.add_compound(Compound("%s%d" % (options.compound_prefix, i)))
    # choose a number of reactions as reversible
    reversibles = random.sample(xrange(reactions), reversible)
    reversibles.sort()
    for i in xrange(reactions):
        if len(reversibles) > 0 and i == reversibles[0]:
            del reversibles[0]
            graph.add_reaction(Reaction("%s%d" % (options.reaction_prefix, i),\
                (), (), (), reversible=True))
        else:
            graph.add_reaction(Reaction("%s%d" % (options.reaction_prefix, i),\
                (), (), (), reversible=False))
    for src in graph.compounds:
        for tar in graph.reactions:
            if random.random() < p:
                logger.debug("Adding edge %s-%s.", src.identifier,\
                    tar.identifier)
                graph.add_edge(src, tar, factor=0)
            # a conditional case here (elif not if) because we cannot determine
            # substrates and products from bidirectional edges
            elif random.random() < p:
                logger.debug("Adding edge %s-%s.", tar.identifier,\
                    src.identifier)
                graph.add_edge(tar, src, factor=0)
    prune_network(graph, logger)
    return graph
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 scale_free_metabolic_network(compounds, reactions, reversible, m, n):
    """
    Uses a Barabasi-Alberts-like preferential attachment algorithm. Adopted from
    the networkx implementation.

    @param m: How many compounds a new reaction node links to

    @param n: How many reactions a new compound node links to
    """
    options = OptionsManager()
    logger = logging.getLogger("%s.scale_free_metabolic_network"\
        % (options.main_logger_name))
    graph = BipartiteMetabolicNetwork(name="scale-free model")
    # target nodes for reactions
    rxn_targets = []
    for i in xrange(m):
        comp = Compound("%s%d" % (options.compound_prefix, i))
        graph.add_compound(comp)
        rxn_targets.append(comp)
#    logger.debug("Targets for reactions: %s", rxn_targets)
    # target nodes for compounds
    comp_targets = []
    # biased lists for preferential attachment
    repeated_cmpds = []
    repeated_rxns = []
    # choose a number of reactions as reversible
    reversibles = random.sample(xrange(reactions), reversible)
    reversibles.sort()
    for i in xrange(n):
        if len(reversibles) > 0 and i == reversibles[0]:
            del reversibles[0]
            rxn = Reaction("%s%d" % (options.reaction_prefix, i),\
                (), (), (), reversible=True)
        else:
            rxn = Reaction("%s%d" % (options.reaction_prefix, i),\
                (), (), (), reversible=False)
        graph.add_reaction(rxn)
        comp_targets.append(rxn)
        for comp in rxn_targets:
            if random.random() < 0.5:
                logger.debug("Adding edge %s -> %s.", rxn.identifier,\
                    comp.identifier)
                graph.add_edge(rxn, comp, factor=0)
            else:
                logger.debug("Adding edge %s -> %s.", comp.identifier,\
                    rxn.identifier)
                graph.add_edge(comp, rxn, factor=0)
        repeated_cmpds.extend(rxn_targets)
        repeated_rxns.extend([rxn] * m)
#    logger.debug("Targets for compounds: %s", comp_targets)
    # current vertices being added
    current_rxn = n
    current_comp = m
    # to prevent reactions from consuming and producing the same compound
    new_targets = list()
    rm_targets = list()
    while (current_comp < compounds or current_rxn < reactions):
        if current_comp < compounds:
            source = Compound("%s%d" % (options.compound_prefix, current_comp))
            graph.add_compound(source)
            for rxn in comp_targets:
                if random.random() < 0.5:
                    logger.debug("Adding edge %s -> %s.", source.identifier,\
                        rxn.identifier)
                    graph.add_edge(source, rxn, factor=0)
                else:
                    logger.debug("Adding edge %s -> %s.", rxn.identifier,\
                        source.identifier)
                    graph.add_edge(rxn, source, factor=0)
            repeated_rxns.extend(comp_targets)
            repeated_cmpds.extend([source] * n)
            comp_targets = set()
            while len(comp_targets) < n:
                rxn = random.choice(repeated_rxns)
                comp_targets.add(rxn)
            current_comp += 1
        if current_rxn < reactions:
            if len(reversibles) > 0 and current_rxn == reversibles[0]:
                del reversibles[0]
                source = Reaction("%s%d" % (options.reaction_prefix,\
                    current_rxn), (), (), (), reversible=True)
            else:
                source = Reaction("%s%d" % (options.reaction_prefix,\
                    current_rxn), (), (), (), reversible=False)
            graph.add_reaction(source)
            new_targets = list()
            rm_targets = list()
            for comp in rxn_targets:
                # same compound may not be in substrates and products
                if (comp in graph.predecessors(source)) or (comp in graph.successors(source)):
                    cmpd = random.choice(repeated_cmpds)
                    while cmpd in graph.predecessors(source) or cmpd in graph.successors(source) or cmpd in new_targets or cmpd in rxn_targets:
                        cmpd = random.choice(repeated_cmpds)
                    new_targets.append(cmpd)
                    rm_targets.append(comp)
                    continue
                if random.random() < 0.5:
                    logger.debug("Adding edge %s -> %s.", source.identifier,\
                        comp.identifier)
                    graph.add_edge(source, comp, factor=0)
                else:
                    logger.debug("Adding edge %s -> %s.", comp.identifier,\
                        source.identifier)
                    graph.add_edge(comp, source, factor=0)
            for comp in rm_targets:
                rxn_targets.remove(comp)
            for comp in new_targets:
                rxn_targets.add(comp)
                if random.random() < 0.5:
                    logger.debug("Adding edge %s -> %s.", source.identifier,\
                        comp.identifier)
                    graph.add_edge(source, comp, factor=0)
                else:
                    logger.debug("Adding edge %s -> %s.", comp.identifier,\
                        source.identifier)
                    graph.add_edge(comp, source, factor=0)
            repeated_cmpds.extend(rxn_targets)
            repeated_rxns.extend([source] * m)
            rxn_targets = set()
            while len(rxn_targets) < m:
                comp = random.choice(repeated_cmpds)
                rxn_targets.add(comp)
            current_rxn += 1
    prune_network(graph, logger)
    return graph