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
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)
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)
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
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