def dijkstra_PQ(G, start, target): P = {start} # Visited Nodes S = PriorityQueue() # Nodes to visit D = {} # Current SP to each node p = {} # Current best parent for each node # Initialise D, p and S for n in G.nodes(): if n == start: D[n] = 0 else: if G.has_edge(start, n): D[n] = G[start][n]["weight"] else: D[n] = math.inf p[n] = start # S is updated with each node and the current SP to each node. The SP value is also # called the priority of that node S.add_task(n, D[n]) # Begin Dijkstra search - The algorithm continues visiting nodes until the target node is # visited and processed alg_continue = True while alg_continue != False: # pop_task() removes and returns the lowest priority task u, Du = S.pop_task() if u in P: continue # Node is added to the list of seen nodes P P.add(u) # All neighbours of u, not already visited are now processed for v, Dv in S: if v in P: continue if G.has_edge(u, v): if D[v] > D[u] + G[u][v]["weight"]: D[v] = D[u] + G[u][v]["weight"] p[v] = u S.add_task(v, D[v]) # Stopping Criteria - If the target node has been added to P (i.e. processed) # then the algorithm stopping criteria is updated and the while loop terminates if target in P: alg_continue = False else: continue # Create Shortest Path next = target SP = deque([target]) while next != start: for i, k in p.items(): if i == next: SP.appendleft(k) next = k return list(SP)
def a_star_tree_search(problem, heuristic_function): # Initiate return variables num_nodes_expanded = 0 current_ply_depth = 0 action_tree_edges = {} state_incrementer = 0 # Initiate loop variables estimated_total_cost = 0 # Value is irrelevant for root node since always popped first cost_to_current_ply = 0 parent_action = None queue = PriorityQueue() queue.add_task((problem.initial_state, state_incrementer, current_ply_depth, cost_to_current_ply, parent_action), estimated_total_cost) while not queue.is_empty(): # Track the number of nodes expanded if num_nodes_expanded % 10000 == 0: print('Number of nodes expanded: ', num_nodes_expanded) num_nodes_expanded += 1 # Get the next node to explore state, state_id, current_ply_depth, cost_to_current_ply, parent_action = queue.pop_task() # Test for the goal state, with admissible heuristic this is guaranteed to be the optimal solution if problem.goal_test_function(state): action_sequence = extract_action_sequence(action_edges=action_tree_edges, terminating_action=parent_action) return state, num_nodes_expanded, current_ply_depth, action_sequence else: # Expand the current nodes and add children to the queue for child_action in problem.get_applicable_actions(state): state_incrementer +=1 child_state_id = state_incrementer # Store the action edge action_tree_edges[child_action] = parent_action # Generate the child state result = child_action.execute(state) child_state = result.state # Calculate the cost to this point cost_to_child_ply = cost_to_current_ply + result.cost estimated_total_cost = cost_to_child_ply + heuristic_function(child_state) # Insert next node into the queue child_ply_depth = current_ply_depth + 1 queue.add_task((child_state, child_state_id, child_ply_depth, cost_to_child_ply, child_action), estimated_total_cost) # If no solution is found return None return None, num_nodes_expanded
def find_ldag(G, v, theta, Ew) -> nx.DiGraph: ''' 有向非巡回グラフとなるような部分グラフを作る INPUT: G -- networkの 有向グラフ v -- 有向グラフのノード theta -- 閾値θ Ew -- グラフG内のエッジ間の重要度 OUTPUT: D -- 有向グラフとなるような部分グラフ(network DiGraph) ''' Inf = PQ() Inf.add_task(v, -1) x, priority = Inf.pop_item() M = -priority X = [x] D = nx.DiGraph() while M >= theta: # あるノードの影響度が閾値θを超えるまで繰り返す out_edges = G.out_edges( [x], data=True) # 終点がノードxとなるエッジを求める[(hoge, x),(fuga, x), …()]のように求められる for (v1, v2, edata) in out_edges: if v2 in X: D.add_edge( v1, v2, weight=edata['weight'] ) # ノードxが有向非巡回グラフの中に含まれていないが,最も影響度が大きい場合は有向非巡回グラフに追加 in_edges = G.in_edges( [x]) # 始点がノードxとなるエッジを求める[(x, hoge), (x, fuga), …]のように求められる. for (u, _) in in_edges: if u not in X: try: [pr, _, _] = Inf.entry_finder[u] except KeyError: pr = 0 # ノードu,v間に複数のエッジがあることを想定し,エッジの数×重み = Inf(u,v) = Σ w(u,x)* Inf(x,v)としている. Inf.add_task(u, pr - G[u][x]['weight'] * Ew[(u, x)] * M) try: x, priority = Inf.pop_item() except KeyError: return D M = -priority X.append(x) # ノードxを有向非巡回グラフのインフルエンサーとして追加 return D
def degreeDiscount(G, k): ''' Finds initial set of nodes to propagate in Independent Cascade model (with priority queue) Input: G -- networkx graph object k -- number of nodes needed Output: S -- chosen k nodes ''' S = [] dd = PQ() # degree discount t = dict() # number of adjacent vertices that are in S d = dict() # degree of each vertex p = dict() # propagation probability in each user for u in G: p[u] = np.random.random() # initialize degree discount for u in G.nodes(): d[u] = sum([G[u][v]['weight'] for v in G[u]]) # each edge adds degree 1 # d[u] = len(G[u]) # each neighbor adds degree 1 dd.add_task(u, -d[u]) # add degree of each node t[u] = 0 # add vertices to S greedily for i in range(k): u, priority = dd.pop_item( ) # extract node with maximal degree discount S.append(u) for v in G[u]: if v not in S: t[v] += G[u][v][ 'weight'] # increase number of selected neighbors priority = d[v] - 2 * t[v] - ( d[v] - t[v]) * t[v] * p[v] # discount of degree dd.add_task(v, -priority) return S
def degreeDiscount(G, k, probability_fixed=True): """ Input: G: 無向 or 有向グラフ k: 求めたいインフルエンサーの数 probability_fixed: 自分の友人に影響を与えることに成功する確率pを全ユーザー共通にするかどうか(True: 共通にする.False:しない) Output S: インフルエンサーの集合 |S| = k """ S = [] dd = PQ() # degree discount t = dict() # number of adjacent vertices that are in S d = dict() # degree of each vertex # initialize degree discount for u in G.nodes(): d[u] = np.sum([G[u][v]['weight'] for v in G[u]]) # each edge adds degree 1 dd.add_task(u, -d[u]) # add degree of each node t[u] = 0 if probability_fixed: p = 0.01 # add vertices to S greedily for i in range(k): u, priority = dd.pop_item() # extract node with maximal degree discount S.append(u) for v in G[u]: if v not in S: t[v] += G[u][v]['weight'] # increase number of selected neighbors (t_v = t_v + 1) priority = d[v] - 2*t[v] - (d[v] - t[v])*t[v]*p # discount of degree dd.add_task(v, -priority) return S else: p = dict() # propagation probability in each user for u in G: p[u] = np.random.uniform(0, 1) # add vertices to S greedily for i in range(k): u, priority = dd.pop_item() # extract node with maximal degree discount S.append(u) for v in G[u]: if v not in S: t[v] += G[u][v]['weight'] # increase number of selected neighbors priority = d[v] - 2*t[v] - (d[v] - t[v])*t[v]*p[v] # discount of degree dd.add_task(v, -priority) return S
def bi_dijkstra_PQ(G, start, target): # All the same data structures are used, except now there are structures for both the # forward and backward directions P_f = {start} P_b = {target} S_f = PriorityQueue() S_b = PriorityQueue() D_f = {} D_b = {} p_f = {} p_b = {} # Forward Initialisation for n in G.nodes(): if n == start: D_f[n] = 0 else: if G.has_edge(start, n): D_f[n] = G[start][n]["weight"] else: D_f[n] = math.inf p_f[n] = start S_f.add_task(n, D_f[n]) # Backward Initialisation for n in G.nodes(): if n == target: D_b[n] = 0 else: if G.has_edge(target, n): D_b[n] = G[target][n]["weight"] else: D_b[n] = math.inf p_b[n] = target S_b.add_task(n, D_b[n]) alg_continue = True while alg_continue != False: # Forward # pop_task() removes and returns the lowest priority task u, Du = S_f.pop_task() if u in P_f: continue P_f.add(u) for v, Dv in S_f: if v in P_f: continue if G.has_edge(u, v): if D_f[v] > D_f[u] + G[u][v]["weight"]: D_f[v] = D_f[u] + G[u][v]["weight"] p_f[v] = u S_f.add_task(v, D_f[v]) if u in P_b: alg_continue = False w = u continue else: pass # Backward # pop_task() removes and returns the lowest priority task u, Du = S_b.pop_task() if u in P_b: continue P_b.add(u) for v, Dv in S_b: if v in P_b: continue if G.has_edge(u, v): if D_b[v] > D_b[u] + G[u][v]["weight"]: D_b[v] = D_b[u] + G[u][v]["weight"] p_b[v] = u S_b.add_task(v, D_b[v]) if u in P_f: alg_continue = False w = u continue else: pass # The SP at the visited node is calculated min_dist = D_f[w] + D_b[w] # All nodes are now visited in both the forward and backward direction # The distance to these numbers in both directions are added and compared with the minimum # distance to the stopping node. If the new distance is smaller, the node is on the SP SP_node = w for i in G.nodes(): if D_f[i] + D_b[i] < min_dist: min_dist = D_f[i] + D_b[i] SP_node = i SP = deque() else: SP = deque([SP_node]) # The shortest path is created by going through all parent nodes from the min distance connection # node in both the forward and backward direction next = SP_node while next != start: for i, k in p_f.items(): if i == next: SP.appendleft(k) next = k next = SP_node while next != target: for i, k in p_b.items(): if i == next: SP.append(k) next = k return list(SP)
def LDAG_heuristic(G, Ew, k, theta): ''' LDAG algorithm for seed selection. Input: G -- directed graph (nx.DiGraph) Ew -- inlfuence weights of edges (eg. uniform, random) (dict) k -- size of seed set (int) t -- parameter theta for finding LDAG (0 <= t <= 1; typical value: 1/320) (int) Output: S -- seed set (list) ''' # define variables S = [] IncInf = PQ() for node in G: IncInf.add_task(node, 0) LDAGs = dict() InfSet = dict() ap = dict() A = dict() for v in G: LDAGs[v] = find_ldag(G, v, theta, Ew) # update influence set for each node in LDAGs[v] with its root for u in LDAGs[v]: InfSet.setdefault(u, []).append(v) alpha = computeAlpha(LDAGs[v], Ew, S, v) A.update(alpha) # add new linear coefficients to A # update incremental influence of all nodes in LDAGs[v] with alphas for u in LDAGs[v]: ap[( v, u )] = 0 # additionally set initial activation probability (line 7) priority, _, _ = IncInf.entry_finder[ u] # find previous value of IncInf IncInf.add_task(u, priority - A[(v, u)]) # and add alpha for it in range(k): s, priority = IncInf.pop_item( ) # 活性化させたノード数の多かったインフルエンサーs(α_v(u)の総和が最も多くなるようなノードv)を求める for v in InfSet[s]: # インフルエンサーsによって活性化したノードv if v not in S: # ノードvがインフルエンサーの集合Sに存在しなければ # ノードvを前処理で求めたグラフの中に入れて,係数を更新 D = LDAGs[v] alpha_v_s = A[(v, s)] dA = computeAlpha(D, Ew, S, s, val=-alpha_v_s) for (s, u) in dA: if u not in S + [ s ]: # don't update IncInf if it's already in S A[(v, u)] += dA[(s, u)] priority, _, _ = IncInf.entry_finder[ u] # find previous value of incremental influence of u IncInf.add_task( u, priority - dA[(s, u)] * (1 - ap[(v, u)])) # and update it accordingly # update ap_v_u for all u reachable from s in D dap = computeActProb(D, Ew, S + [s], s, val=1 - ap[(v, s)]) for (s, u) in dap: # ノードのsがノードuに影響を及ぼす確率dapに対して if u not in S + [s]: # ノードuがインフルエンサーでなければ ap[(v, u)] += dap[(s, u)] priority, _, _ = IncInf.entry_finder[ u] # find previous value of incremental influence of u IncInf.add_task( u, priority + A[(v, u)] * dap[(s, u)]) # and update it accordingly S.append(s) return S