Пример #1
0
def lp_model(cg: ComputationGraph,
             agentsdef: Iterable[AgentDef],
             footprint: Callable[[str], float],
             capacity: Callable[[str], float],
             route: Callable[[str, str], float],
             msg_load: Callable[[str, str], float],
             hosting_cost: Callable[[str, str], float]):

    comp_names = [n.name for n in cg.nodes]
    agt_names = [a.name for a in agentsdef]
    pb = LpProblem('ilp_compref', LpMinimize)

    # One binary variable xij for each (variable, agent) couple
    xs = LpVariable.dict('x', (comp_names, agt_names), cat=LpBinary)

    # One binary variable for computations c1 and c2, and agent a1 and a2
    betas = {}
    count = 0
    for a1, a2 in combinations(agt_names, 2):
        # Only create variables for couple c1, c2 if there is an edge in the
        # graph between these two computations.
        for l in cg.links:
            # As we support hypergraph, we may have more than 2 ends to a link
            for c1, c2 in combinations(l.nodes, 2):
                count += 2
                b = LpVariable('b_{}_{}_{}_{}'.format(c1, a1, c2, a2),
                               cat=LpBinary)
                betas[(c1, a1, c2, a2)] = b
                pb += b <= xs[(c1, a1)]
                pb += b <= xs[(c2, a2)]
                pb += b >= xs[(c2, a2)] + xs[(c1, a1)] - 1

                b = LpVariable('b_{}_{}_{}_{}'.format(c1, a2, c2, a1),
                               cat=LpBinary)
                betas[(c1, a2, c2, a1)] = b
                pb += b <= xs[(c2, a1)]
                pb += b <= xs[(c1, a2)]
                pb += b >= xs[(c1, a2)] + xs[(c2, a1)] - 1

    # Set objective: communication + hosting_cost
    pb += _objective(xs, betas, route, msg_load, hosting_cost), \
        'Communication costs and prefs'

    # Adding constraints:
    # Constraints: Memory capacity for all agents.
    for a in agt_names:
        pb += lpSum([footprint(i) * xs[i, a] for i in comp_names])\
              <= capacity(a), \
              'Agent {} capacity'.format(a)

    # Constraints: all computations must be hosted.
    for c in comp_names:
        pb += lpSum([xs[c, a] for a in agt_names]) == 1, \
            'Computation {} hosted'.format(c)

    # solve using GLPK
    status = pb.solve(solver=GLPK_CMD(keepFiles=1, msg=False,
                                      options=['--pcost']))

    if status != LpStatusOptimal:
        raise ImpossibleDistributionException("No possible optimal"
                                              " distribution ")
    logger.debug('GLPK cost : %s', value(pb.objective))

    # print('BETAS:')
    # for c1, a1, c2, a2 in betas:
    #     print('  ', c1, a1, c2, a2, value(betas[(c1, a1, c2, a2)]))
    #
    # print('XS:')
    # for c, a in xs:
    #     print('  ', c, a, value(xs[(c, a)]))

    mapping = {}
    for k in agt_names:
        agt_computations = [i for i, ka in xs
                            if ka == k and value(xs[(i, ka)]) == 1]
        # print(k, ' -> ', agt_computations)
        mapping[k] = agt_computations
    return mapping
Пример #2
0
def distribute_factors(
    agents: Dict[str, AgentDef],
    cg: ComputationGraph,
    footprints: Dict[str, float],
    mapping: Dict[str, List[str]],
    msg_load: Callable[[str, str], float],
) -> Dict[str, List[str]]:
    """
    Optimal distribution of factors on agents.

    Parameters
    ----------
    cg: computations graph

    agents: dict
        a dict {agent_name : AgentDef} containing all available agents

    Returns
    -------
    a dict { agent_name: list of factor names}
    """
    pb = LpProblem("ilp_factors", LpMinimize)

    # build the inverse mapping var -> agt
    inverse_mapping = {}  # type: Dict[str, str]
    for a in mapping:
        inverse_mapping[mapping[a][0]] = a

    # One binary variable xij for each (variable, agent) couple
    factor_names = [n.name for n in cg.nodes if isinstance(n, FactorComputationNode)]
    xs = LpVariable.dict("x", (factor_names, agents), cat=LpBinary)
    logger.debug("Binary variables for factor distribution : %s", xs)

    # Hard constraints: respect agent's capacity
    for a in agents:
        # Footprint of the variable this agent is already hosting:
        v_footprint = footprints[mapping[a][0]]
        pb += (
            lpSum([footprints[fn] * xs[fn, a] for fn in factor_names])
            <= (agents[a].capacity - v_footprint),
            "Agent {} capacity".format(a),
        )

    # Hard constraints: all computations must be hosted.
    for c in factor_names:
        pb += lpSum([xs[c, a] for a in agents]) == 1, "Factor {} hosted".format(c)

    # 1st objective : minimize communication costs:
    comm = LpAffineExpression()
    for (fn, an_f) in xs:
        for vn in cg.neighbors(fn):
            an_v = inverse_mapping[vn]  # agt hosting neighbor var vn
            comm += agents[an_f].route(an_v) * msg_load(vn, fn) * xs[(fn, an_f)]

    # 2st objective : minimize hosting costs
    hosting = lpSum([agents[a].hosting_cost(c) * xs[(c, a)] for c, a in xs])

    # agregate the two objectives using RATIO_HOST_COMM
    pb += lpSum([RATIO_HOST_COMM * comm, (1 - RATIO_HOST_COMM) * hosting])

    # solve using GLPK and convert to mapping { agt_name : [factors names]}
    status = pb.solve(solver=GLPK_CMD(keepFiles=1, msg=False, options=["--pcost"]))
    if status != LpStatusOptimal:
        raise ImpossibleDistributionException(
            "No possible optimal distribution for factors"
        )
    logger.debug("GLPK cost : %s", value(pb.objective))
    mapping = {}  # type: Dict[str, List[str]]
    for k in agents:
        agt_computations = [i for i, ka in xs if ka == k and value(xs[(i, ka)]) == 1]
        # print(k, ' -> ', agt_computations)
        mapping[k] = agt_computations
    logger.debug("Factors distribution : %s ", mapping)
    return mapping