def improve_init_sol(adj_table, init_sol):
    """
    :param adj_table: {node_id: {nbor_id: weight}}

    :param init_sol:  [..., node_k, ...]

    :return: Total dist of the improved solution. Note we will modify the init_sol in place.
    """

    ttl_dist = get_ttl_dist(adj_table, init_sol)
    last_update_idx = None

    while True:
        variation = 0
        for idx in range(0, len(init_sol)):
            new_ttl_dist = get_coordinate_improvement(adj_table, init_sol,
                                                      ttl_dist, idx)
            """
            get_coordinate_improvement only find a candidate to 
            improve, not the best candidate. Thus, if idx == last_update_idx,
            we also need to check whether there is an improvement at idx.
            """
            if new_ttl_dist < ttl_dist:
                last_update_idx = idx
            elif new_ttl_dist == ttl_dist and idx == last_update_idx:
                break

            variation += (ttl_dist - new_ttl_dist)
            ttl_dist = new_ttl_dist

        # print 'step: ' + str(step) + ' \t variation: ' + str(variation)
        if variation == 0:
            break

    return ttl_dist
Example #2
0
def get_penalty(adj_table, monitors):
    """
    :param adj_table: {node:{nbor: weight}}
    :param monitors: [... monitor_j ...]
    :return:
    """
    return [get_ttl_dist(adj_table, monitor) for monitor in monitors]
def maximize_degree_discount_heuristic(adj_table, maximal_monitor_num):
    """
    :param adj_table: {node_id: {nbor_id: weight}}
    :param maximal_monitor_num:
    :return: Choose nodes to cover as many edges as possible
    """
    
    node_deg_map = get_node_deg_map(adj_table, adj_table.iterkeys()) 

    """
    Heap in python is min-heap. We want to find nodes with largest degree.
    Thus we push neg-degree into heap.
    """
    h = []
    for node in node_deg_map:
        heapq.heappush(h, (-1 * node_deg_map[node], node))

    monitors = []

    # {node_id: degree reduction}
    node_deg_reduction_map = {}
    while True:
        neg_deg, node = heapq.heappop(h)

        """
        if node in node_deg_reduction_map, then we need to adjust the degree of node to be:
        degree = degree - node_deg_reduction_map
        We store (-1) * degree in heap. 
        Thus, (-1) * degree = (-1) * degree + node_deg_reduction_map
        """
        if node in node_deg_reduction_map:
            neg_deg += node_deg_reduction_map[node]
            del node_deg_reduction_map[node]
            heapq.heappush(h, (neg_deg, node))
            continue

        """
        if node not in node_deg_reduction_map, then we add node to monitor and 
        update the node_deg_reduction_map. 
        """
        monitors.append(node)
        if len(monitors) == maximal_monitor_num:
            break

        for nbor in adj_table[node]:
            node_deg_reduction_map[nbor] = node_deg_reduction_map.setdefault(nbor, 0) + adj_table[node][nbor]

    return monitors, get_ttl_dist(adj_table, monitors)
Example #4
0
def maximize_comb_local_centrality(adj_table, maximal_monitor_num):
    """
    :param adj_table: {node:{nbor:weight}}
    :param maximal_monitor_num:
    :return: Find maximal_monitor_num nodes to maximize the combinatorial local centrality,
    and write the result into central_node_path
    """
    """
    node_weight_map = {node: weight}
    For u, weight(u) = \sum_{w \in Nbor(u)} N(w)
    """
    node_weight_map = get_node_weight_map(adj_table)

    # nodes that maximize the combinatorial local centrality
    central_nodes = []
    # neighbors of the chosen nodes
    nbors = set([])

    node_marginal_reward_map = dict(
        zip(adj_table.iterkeys(), [float('inf')] * len(adj_table)))

    while True:
        if len(central_nodes) == maximal_monitor_num:
            break

        marginal_reward = 0
        chosen_node = None
        for node in node_marginal_reward_map:

            if node_marginal_reward_map[node] > marginal_reward:
                tmp_marginal_reward = get_marginal_reward(
                    adj_table, node, nbors, node_weight_map)

                node_marginal_reward_map[node] = tmp_marginal_reward

                if tmp_marginal_reward > marginal_reward:
                    marginal_reward = tmp_marginal_reward
                    chosen_node = node

        if marginal_reward == 0:
            break

        del node_marginal_reward_map[chosen_node]
        central_nodes.append(chosen_node)
        nbors.update(adj_table[chosen_node].iterkeys())
    return central_nodes, get_ttl_dist(adj_table, central_nodes)
Example #5
0
def improve_init_sol(adj_table, init_sol, maximal_iter_step, candidate_num):
    """
    :param adj_table: {node_id: {nbor_id: weight}}

    :param init_sol:  [..., node_k, ...]

    :param maximal_iter_step:  For init_sol, we improve it at most iter_num steps.
    For each step, we improve along 0, 1, ..., len(init_sol)-1 index respectively.

    :param candidate_num:
    :return: Total dist of the improved solution. Note we will modify the init_sol in place.
    """
    """
    It is important to shallow copy the init_sol. 
    We can't modify the init_sol.
    """
    step = 0
    ttl_dist = get_ttl_dist(adj_table, init_sol)

    last_update_idx = None
    while True:
        variation = 0
        for idx in range(0, len(init_sol)):
            """
            In the get_coordinate_improvement, we find the best candidate.
            idx = last_update_idx means that no change will happen.  
            """
            if idx == last_update_idx:
                break

            new_ttl_dist = get_coordinate_improvement(adj_table, init_sol,
                                                      ttl_dist, candidate_num,
                                                      idx)
            if new_ttl_dist < ttl_dist:
                last_update_idx = idx

            variation += (ttl_dist - new_ttl_dist)
            ttl_dist = new_ttl_dist

        # print 'step: ' + str(step) + ' \t variation: ' + str(variation)
        step += 1
        if variation <= stop_criteria or step == maximal_iter_step:
            break

    return ttl_dist
Example #6
0
def get_init_sol(adj_table, node_ttl_dist_map, max_monitor_num):
    """
    :param adj_table: {node_id: {nbor_id: weight}}

    :param node_ttl_dist_map: path of node_ttl_dist_map
    {node_id: total distance from node_id to other nodes}

    :param max_monitor_num:

    :return: initial solution
    """
    node_num = len(adj_table)
    candidate_num = get_init_sol_candidate_num(node_num)
    """
    penalty(M) is total distance from M to other nodes
    when M = \empty, penalty is |V|^2
    """
    penalty_of_empty = node_num**2
    """
    The candidates for the first node is the k = candidate_num nodes with largest marginal reward
    for a node u, delta(u) = R(u) - R(empty) = penalty(empty)-penalty(u)
    Thus, it is equivalent to find k nodes with smallest penalty.
    """
    candidates = heapq.nsmallest(candidate_num, node_ttl_dist_map,
                                 node_ttl_dist_map.get)

    # heap to store the negative of marginal reward
    h = []
    # the marginal reward of u is updated <=> u in updated.
    updated = {}
    """
    a tried node u means that u has been updated at least once.
    u in the heap or u chosen as a monitor <=> u in tried_nodes
    """
    tried_nodes = {}
    """
    Assume we have chosen set M, in next chosen_monitor_num, 
    we need to choose u, such that delta(u) = R({u} \union M) - R(M) is maximized.
    Thus we store -1 * delta(u) = penalty({u} \union M) - penalty(M)
    in the min-heap of python.
    We adopt the lazy-forward updating.
    """
    """
    Initialize heap. M = \empty
    """
    for node in candidates:
        heapq.heappush(
            h, (get_ttl_dist(adj_table, [node]) - penalty_of_empty, node))
        updated[node] = None
        tried_nodes[node] = None

    monitors = []
    penalty = penalty_of_empty

    while h:
        neg_delta, node = heapq.heappop(h)
        """
        IF the node hasn't been updated, update it and put back.
        ELSE the node has the largest marginal reward.
        """
        if node not in updated:
            # neg_delta = get_ttl_dist(adj_table, monitors + [node]) - penalty
            neg_delta = fast_get_ttl_dist(adj_table, node_root_dist_map,
                                          penalty, [node]) - penalty

            heapq.heappush(h, (neg_delta, node))
            updated[node] = None
            continue
        """
        Put the node in monitors 
        penalty(M\union {u}) = penalty(M) - delta
        """
        monitors.append(node)
        penalty += neg_delta
        if len(monitors) == max_monitor_num:
            break
        """
        Generate new candidates for chosen monitors.
        Note that: u in tried_nodes <=> u is in the heap or chosen as a verify_monitor
        in either case, we do not need to calculate marginal reward and put into
        the heap. 
        """
        updated = {}

        level_net = get_random_lev_net(adj_table, monitors, seed=1)

        candidates = get_candidates(adj_table, level_net, candidate_num)
        node_root_dist_map = get_node_root_dist_map(level_net)

        for candidate in candidates:
            if candidate in tried_nodes:
                continue
            neg_delta = fast_get_ttl_dist(adj_table, node_root_dist_map,
                                          penalty, [candidate]) - penalty

            heapq.heappush(h, (neg_delta, candidate))
            updated[candidate] = None
            tried_nodes[candidate] = None

    return monitors, penalty