Exemplo n.º 1
0
def distribute(
    computation_graph: ComputationsFactorGraph,
    agentsdef: Iterable[AgentDef],
    hints=None,
    computation_memory: Callable[[ComputationNode], float] = None,
    communication_load: Callable[[ComputationNode, str], float] = None,
    timeout=600,  # Max 10 min
) -> Distribution:
    if computation_memory is None or communication_load is None:
        raise ImpossibleDistributionException(
            "oilp_secp_fgdp distribution requires "
            "computation_memory and link_communication functions"
        )

    mapping = defaultdict(lambda: [])
    agents_capa = {a.name: a.capacity for a in agentsdef}
    variable_computations, factor_computations = [], []
    for comp in computation_graph.nodes:
        if isinstance(comp, VariableComputationNode):
            variable_computations.append(comp.name)
        elif isinstance(comp, FactorComputationNode):
            factor_computations.append(comp.name)
        else:
            raise ImpossibleDistributionException(
                f"Error: {comp} is neither a factor nor a variable computation"
            )
    # actuators variables and cost factor on the corresponding agent:
    for variable in variable_computations[:]:

        for agent in agentsdef:
            if agent.hosting_cost(variable) == 0:
                # Found an actuator variable, host it on the agent
                mapping[agent.name].append(variable)
                variable_computations.remove(variable)
                agents_capa[agent.name] -= computation_memory(
                    computation_graph.computation(variable)
                )
                # search for the cost factor, if any, and host it on the same agent.
                for factor in factor_computations[:]:
                    if f"c_{variable}" == factor:
                        mapping[agent.name].append(factor)
                        factor_computations.remove(factor)
                        agents_capa[agent.name] -= computation_memory(
                            computation_graph.computation(factor)
                        )
                if agents_capa[agent.name] < 0:
                    raise ImpossibleDistributionException(
                        f"Not enough capacity on {agent} to hosts actuator {variable}: {agents_capa[agent.name]}"
                    )
                break
    logger.info(f"Actuator variables - agents: {dict(mapping)}")
    logger.info(f"Remaining capacity: {dict(agents_capa)}")

    return fg_secp_ilp(
        computation_graph,
        agentsdef,
        Distribution(mapping),
        computation_memory,
        communication_load,
    )
Exemplo n.º 2
0
def secp_dist_objective_function(cg: ComputationsFactorGraph,
                                 communication_load, alphas, agents_names):
    # The objective function is the negated sum of the communication cost on
    # the links in the factor graph.
    return lpSum([
        -communication_load(cg.computation(link.variable_node),
                            link.factor_node) *
        alphas[((link.variable_node, link.factor_node), k)]
        for link in cg.links for k in agents_names
    ])
Exemplo n.º 3
0
def distribution_cost(
    distribution: Distribution,
    computation_graph: ComputationsFactorGraph,
    agentsdef: Iterable[AgentDef],
    computation_memory: Callable[[ComputationNode], float],
    communication_load: Callable[[ComputationNode, str], float],
) -> float:
    """
    Compute the cost of the distribution.

    Only takes communication costs into account (no hosting nor route costs).

    Parameters
    ----------
    distribution
    computation_graph
    agentsdef
    computation_memory
    communication_load

    Returns
    -------

    """
    comm = 0
    agt_names = [a.name for a in agentsdef]
    for l in computation_graph.links:
        # As we support hypergraph, we may have more than 2 ends to a link
        for c1, c2 in combinations(l.nodes, 2):
            if distribution.agent_for(c1) != distribution.agent_for(c2):
                edge_cost = communication_load(
                    computation_graph.computation(c1), c2)
                logger.debug(f"edge cost between {c1} and {c2} :  {edge_cost}")
                comm += edge_cost
            else:
                logger.debug(
                    f"On same agent, no edge cost between {c1} and {c2}")

    # This distribution model only takes communication cost into account.
    # cost = RATIO_HOST_COMM * comm + (1-RATIO_HOST_COMM) * hosting
    return comm, comm, 0
Exemplo n.º 4
0
def secp_computation_memory_in_cg(computation_name: str,
                                  cg: ComputationsFactorGraph,
                                  computation_memory):
    computation = cg.computation(computation_name)
    l = computation_memory(computation)
    return l
Exemplo n.º 5
0
def distribute(
        computation_graph: ComputationsFactorGraph,
        agentsdef: Iterable[AgentDef],
        hints: DistributionHints = None,
        computation_memory: Callable[[ComputationNode], float] = None,
        communication_load: Callable[[ComputationNode, str], float] = None,
        timeout=None,  # not used
) -> Distribution:
    if computation_memory is None:
        raise ImpossibleDistributionException("adhoc distribution requires "
                                              "computation_memory functions")

    # as we're dealing with a secp modelled as a factor graph, we have computations for
    # actuator and physical model variables, rules and physical model factors.
    mapping = defaultdict(lambda: [])
    agents_capa = {a.name: a.capacity for a in agentsdef}
    variable_computations = []
    factor_computations = []
    for comp in computation_graph.nodes:
        if isinstance(comp, VariableComputationNode):
            variable_computations.append(comp.name)
        elif isinstance(comp, FactorComputationNode):
            factor_computations.append(comp.name)
        else:
            raise ImpossibleDistributionException(
                f"Error: {comp} is neither a factor nor a variable computation"
            )

    # First, put each actuator variable and cost factor on its agent
    for variable in variable_computations[:]:

        for agent in agentsdef:
            if agent.hosting_cost(variable) == 0:
                # Found an actuator variable, host it on the agent
                mapping[agent.name].append(variable)
                variable_computations.remove(variable)
                agents_capa[agent.name] -= computation_memory(
                    computation_graph.computation(variable))
                # search for the cost factor, if any, and host it on the same agent.
                for factor in factor_computations[:]:
                    if f"c_{variable}" == factor:
                        mapping[agent.name].append(factor)
                        factor_computations.remove(factor)
                        agents_capa[agent.name] -= computation_memory(
                            computation_graph.computation(factor))
                if agents_capa[agent.name] < 0:
                    raise ImpossibleDistributionException(
                        f"Not enough capacity on {agent} to hosts actuator {variable}: {agents_capa[agent.name]}"
                    )
                break
    logger.info(f"Actuator computations - agents: {dict(mapping)}")
    logger.info(f"Remaining capacity: {dict(agents_capa)}")

    # now find computations for physical models and variables variables.
    # * all remaining variables are model variables
    # * physical model factor computation names contain the name of the variable
    model_variables = variable_computations
    models = []
    for model_var in model_variables:
        for fact in factor_computations:
            if f"c_{model_var}" == fact:
                models.append((model_var, fact))
                factor_computations.remove(fact)

    # All remaining factor ar rule factors
    rule_factors = factor_computations

    logger.debug(f"Physical models: {models}")
    logger.debug(f"Rules: {rule_factors}")

    # Now place models
    for model_var, model_fac in models:
        footprint = computation_memory(
            computation_graph.computation(model_fac)) + computation_memory(
                computation_graph.computation(model_var))
        neighbors = computation_graph.neighbors(model_fac)

        candidates = find_candidates(agents_capa, model_fac, footprint,
                                     mapping, neighbors)

        # Host the model on the first agent and decrease its remaining capacity
        selected = candidates[0][2]
        mapping[selected].append(model_var)
        mapping[selected].append(model_fac)
        agents_capa[selected] -= footprint
    logger.debug(f"All models hosted: {dict(mapping)}")
    logger.debug(f"Remaining capacity: {agents_capa}")

    # And rules at last:
    for rule_fac in rule_factors:
        footprint = computation_memory(computation_graph.computation(rule_fac))
        neighbors = computation_graph.neighbors(rule_fac)

        candidates = find_candidates(agents_capa, rule_fac, footprint, mapping,
                                     neighbors)

        # Host the computation on the first agent and decrease its remaining capacity
        selected = candidates[0][2]
        mapping[selected].append(rule_fac)
        agents_capa[selected] -= footprint

    return Distribution({a: list(mapping[a]) for a in mapping})