def test_multigraph_steiner_tree(self): with pytest.raises(nx.NetworkXNotImplemented): G = nx.MultiGraph() G.add_edges_from([(1, 2, 0, { 'weight': 1 }), (2, 3, 0, { 'weight': 999 }), (2, 3, 1, { 'weight': 1 }), (3, 4, 0, { 'weight': 1 }), (3, 5, 0, { 'weight': 1 })]) terminal_nodes = [2, 4, 5] expected_edges = [ (2, 3, 1, { 'weight': 1 }), # edge with key 1 has lower weight (3, 4, 0, { 'weight': 1 }), (3, 5, 0, { 'weight': 1 }) ] # not implemented T = steiner_tree(G, terminal_nodes)
def test_multigraph_steiner_tree(self): G = nx.MultiGraph() G.add_edges_from([ (1, 2, 0, { "weight": 1 }), (2, 3, 0, { "weight": 999 }), (2, 3, 1, { "weight": 1 }), (3, 4, 0, { "weight": 1 }), (3, 5, 0, { "weight": 1 }), ]) terminal_nodes = [2, 4, 5] expected_edges = [ (2, 3, 1, { "weight": 1 }), # edge with key 1 has lower weight (3, 4, 0, { "weight": 1 }), (3, 5, 0, { "weight": 1 }), ] T = steiner_tree(G, terminal_nodes) assert_edges_equal(T.edges(data=True, keys=True), expected_edges)
def steinerHeuristic(self, state): """ start with a subtree T consisting of one given terminal vertex while T does not span all terminals select a termoinal x not in T that is closest to a vertex in # T add to T the shortest path that connects x with T """ homes_to_visit = [] for i in range(len(state.homes_locations)): if state.homes_reached[i] == False: homes_to_visit += [state.homes_locations[i]] homes_to_visit.append(state.start) k = tuple(homes_to_visit) if k in self.steinerMemo: result = self.steinerMemo[k] else: result = steiner_tree(self.graph, homes_to_visit, weight='weight').size(weight='weight') self.steinerMemo[k] = result result += state.get_dropoff_cost_and_loc(self.graph)[0] return 2 / 3 * result
def test_steiner_tree(self): S = steiner_tree(self.G, self.term_nodes) expected_steiner_tree = [(1, 2, {'weight': 10}), (2, 3, {'weight': 10}), (2, 7, {'weight': 1}), (3, 4, {'weight': 10}), (5, 7, {'weight': 1})] assert_edges_equal(list(S.edges(data=True)), expected_steiner_tree)
def test_steiner_tree(self): S = steiner_tree(self.G, self.term_nodes) expected_steiner_tree = [(1, 2, {'weight': 10}), (2, 3, {'weight': 10}), (2, 7, {'weight': 1}), (3, 4, {'weight': 10}), (5, 7, {'weight': 1})] assert_edges_equal(list(S.edges(data=True)), expected_steiner_tree)
def test_multigraph_steiner_tree_adjacent(self): terminal_nodes = ['A', 'B'] expected_edges = [ ('A', 'B', 'k1', {'weight': 2}), ] T = steiner_tree(self.M, terminal_nodes) assert_edges_equal(list(T.edges(keys=True, data=True)), expected_edges)
def __init__(self, adj_matrix, homes_arr, soda_loc, locs): self.graph = util170.adjacency_matrix_to_graph(adj_matrix)[0] mapping = dict(zip(self.graph, locs)) self.netx_graph = netx.relabel_nodes(self.graph, mapping) self.distanceMemo = dict() self.start_loc = soda_loc self.homes = homes_arr homes_to_visit = self.homes.copy() homes_to_visit.append(self.start_loc) self.steiner_tree = steiner_tree(self.netx_graph, homes_to_visit, weight='weight')
def test_multigraph_steiner_tree_multiple_terminals(self): terminal_nodes = ['A', 'C', 'H'] expected_edges = [ ('A', 'B', 'k1', {'weight': 2}), ('B', 'C', 'k4', {'weight': 2}), ('C', 'D', 'k6', {'weight': 2}), ('D', 'F', 'k8', {'weight': 2}), ('F', 'H', 'k10', {'weight': 2}) ] T = steiner_tree(self.M, terminal_nodes) assert_edges_equal(list(T.edges(keys=True, data=True)), expected_edges)
def test_multigraph_steiner_tree(self): G = nx.MultiGraph() G.add_edges_from([ (1, 2, 0, {'weight': 1}), (2, 3, 0, {'weight': 999}), (2, 3, 1, {'weight': 1}), (3, 4, 0, {'weight': 1}), (3, 5, 0, {'weight': 1}) ]) terminal_nodes = [2, 4, 5] expected_edges = [ (2, 3, 1, {'weight': 1}), # edge with key 1 has lower weight (3, 4, 0, {'weight': 1}), (3, 5, 0, {'weight': 1}) ] # not implemented T = steiner_tree(G, terminal_nodes)
def get_steiner_tree_edges(sent, pivot_words, ori_concepts): if not len(sent): return set(), 0, 0 sent = sent[:-1].replace( ".", ", and ") + sent[-1] # combine multiple sentences. sent_doc = nlp(sent) dep_tree = nx.Graph() pivot_word_ids = [] covered_pivots = set() covered_pivot_pos = set() for token in nlp(sent): cur_text = token.lemma_ if cur_text not in pivot_words: cur_text = token.pos_ else: covered_pivots.add(cur_text) pivot_word_ids.append(token.i) if token.lemma_ + "_" + token.pos_[0] in ori_concepts: covered_pivot_pos.add(token.lemma_ + "_" + token.pos_[0]) head_text = token.head.lemma_ if head_text not in pivot_words: head_text = token.head.pos_ dep_tree.add_node(token.i, text=cur_text) dep_tree.add_node(token.head.i, text=head_text) dep_tree.add_edge(token.i, token.head.i, dep=token.dep_, weight=1) if not nx.is_connected(dep_tree): # print("not connected: ", sent) return set(), len(covered_pivots) / len(pivot_words), len( covered_pivot_pos) / len(pivot_words) # Find the minimal subgraph that covers all the given nodes. subgraph = steinertree.steiner_tree(dep_tree, pivot_word_ids) edge_set = set() for n1, n2 in subgraph.edges(): n1_text = dep_tree.nodes[n1]["text"] n2_text = dep_tree.nodes[n2]["text"] edge = dep_tree[n1][n2] dep = edge["dep"] if n1_text > n2_text: n1_text, n2_text = n2_text, n1_text edge_set.add("%s->%s->%s" % (n1_text, dep, n2_text)) return edge_set, len(covered_pivots) / len(pivot_words), len( covered_pivot_pos) / len(pivot_words)
def find_steiner_tree( graphs: Collection, terminal_nodes: Collection, metric_closures: typing.List[nx.Graph] = None) -> nx.Graph: terminal_graph = None terminal_metric_closure = None for idx, g in enumerate(graphs): if not all(node in g for node in terminal_nodes): continue terminal_graph = g terminal_metric_closure = metric_closures[ idx] if metric_closures else None if not terminal_graph: raise NoSuitableGraphError() T = steiner_tree(terminal_graph, terminal_nodes, metric_closure=terminal_metric_closure) return T
def find_ontology_subset(datum, table, log=True): question_toks = datum["question_toks"] evidence_nodes = find_evidence_nodes(question_toks, table) spanned_tree = [] gold_ontology_subset = get_gold_ontology_subset(datum["sql"]) candidate_correct = False for candidate_set in evidence_nodes: col_offset = len(table["table_names"]) table_nodes = list(candidate_set["tables"]) col_nodes = [col_num + col_offset for col_num in candidate_set["cols"]] tree = steiner_tree(table["graph"], table_nodes + col_nodes) nodes = tree.nodes() tables = set() cols = set() if not nodes: tables = set(table_nodes) cols = set(col_nodes) for node in nodes: if node < col_offset: tables.add(node) else: cols.add(node - col_offset) spanned_tree.append({'tables': tables, 'cols': cols}) if gold_ontology_subset["tables"] == tables and gold_ontology_subset[ "cols"] == cols: candidate_correct = True if log: print("======================") table_printer(table) print(datum["question"]) print(datum["query"]) print(gold_ontology_subset) print(evidence_nodes) print(spanned_tree) print(candidate_correct) return True, candidate_correct
def get_steiner_tree(self): homes_to_visit = self.homes.copy() homes_to_visit.append(self.start_loc) result = steiner_tree(self.graph, homes_to_visit, weight='weight') return result
def decompose( self, topology: nx.Graph = None ) -> Iterator[Union[CNot, XPow, YPow, ZPow]]: """ Returns a Circuit corresponding to the exponential of the Pauli algebra element object, i.e. exp[-1.0j * alpha * element] If a qubit topology is provided then the returned circuit will respect the qubit connectivity, adding swaps as necessary. """ # Kudos: Adapted from pyquil. The topological network is novel. circ = Circuit() element = self.element alpha = self.alpha if element.is_identity() or element.is_zero(): return circ # pragma: no cover # TESTME # Check that all terms commute groups = pauli_commuting_sets(element) if len(groups) != 1: raise ValueError("Pauli terms do not all commute") for qbs, ops, coeff in element: if not np.isclose(complex(coeff).imag, 0.0): raise ValueError("Pauli term coefficients must be real") theta = complex(coeff).real * alpha if len(ops) == 0: continue # TODO: 1-qubit terms special case active_qubits = set() change_to_z_basis = Circuit() for qubit, op in zip(qbs, ops): active_qubits.add(qubit) if op == "X": change_to_z_basis += Y(qubit)**-0.5 elif op == "Y": change_to_z_basis += X(qubit)**0.5 if topology is not None: if not nx.is_directed(topology) or not nx.is_arborescence( topology): # An 'arborescence' is a directed tree active_topology = steiner_tree(topology, active_qubits) center = nx.center(active_topology)[0] active_topology = nx.dfs_tree(active_topology, center) else: active_topology = topology else: active_topology = nx.DiGraph() nx.add_path(active_topology, reversed(list(active_qubits))) cnot_seq = Circuit() order = list(reversed(list(nx.topological_sort(active_topology)))) for q0 in order[:-1]: q1 = list(active_topology.pred[q0])[0] if q1 not in active_qubits: cnot_seq += Swap(q0, q1) active_qubits.add(q1) else: cnot_seq += CNot(q0, q1) circ += change_to_z_basis circ += cnot_seq circ += Z(order[-1])**(2 * theta / np.pi) circ += cnot_seq.H circ += change_to_z_basis.H # end term loop yield from circ # type: ignore
def determine_inlining_order(clusters, graph): inlinings = [] for cluster in clusters: if len(cluster) == 1: inlinings.append(graph.subgraph(cluster)) continue # The steiner tree is an minimum tree spanning a specified set of nodes in a graph. It will be used to define # the inlining order. The steiner_tree algorithm is only implemented for connected, undirected graphs. # The graph is transformed accordingly. It is not possible to make a subgraph containing only the nodes of a # cluster, because they may be connected by an intermediate, unimportant node. # Only one intermediate node is allowed when inlining two core functions. All non-core nodes, that dont call # a core node are removed to prevent dead ends in the inlining graph, when the directions are restored. reduced_graph = graph.copy() for node in list(graph.nodes()): if node not in cluster and not any( [callee in cluster for callee in graph.successors(node)]): reduced_graph.remove_node(node) inlining = steiner_tree(reduced_graph.to_undirected(), cluster) inlinings.append(inlining) # After the steiner tree has been created, the direction information can now be restored from the original graph directed_inlinings = [] for inlining in inlinings: directed_inlining = nx.DiGraph() if len(inlining) > 1: for from_node in inlining: for to_node in graph.successors(from_node): if to_node in inlining and inlining.has_edge( from_node, to_node): directed_inlining.add_edge(from_node, to_node) else: directed_inlining.add_node(list(inlining.nodes())[0]) # To allow inlining, each cluster must have exactly one root. Additional roots will be moved to a new cluster. roots = get_root_nodes(directed_inlining) if len(roots) > 1: #Other roots may have nodes connected to them, that are not reachable from this root. Those nodes shall #also be moved to the new cluster. for other_root in roots[1:]: this_roots_subgraph = set([ node for node in directed_inlining if nx.has_path(directed_inlining, roots[0], node) ]) other_roots_subgraph = set([ node for node in directed_inlining if nx.has_path(directed_inlining, other_root, node) ]) other_roots_subgraph = other_roots_subgraph - this_roots_subgraph directed_inlining.remove_nodes_from(other_roots_subgraph) other_roots_inlining = determine_inlining_order( [other_roots_subgraph], graph)[0] directed_inlinings.append(other_roots_inlining) root = roots[0] if len(roots) != 0 else None # While the undirected graph was a tree, restoring the direction information may cause a cycle when two # functions call each other, which leads to an infinite loop when inlining. The edge leading towards the # root is deleted, so all nodes remain reachable from the root try: #Search for a root node candidate if none exist due to the restoration of the direction information. #Due to cycles root nodes may be called by other nodes, but only if the root node itself calls that node #through a cycle. The first suitable root node candidate is selected for cycle in nx.simple_cycles(directed_inlining): if root is not None: break assert len(cycle) == 2 for node in cycle: callers = set(directed_inlining.predecessors(node)) callees = set(directed_inlining.successors(node)) if len(callers.difference(callees)) == 0: root = node break for cycle in nx.simple_cycles(directed_inlining): # cycles cannot be longer than 2, otherwise the undirected graph would not be a tree. That is unless # two cycles are adjacent to each other, which is not recogniced by nx.simple_cycles assert len(cycle) == 2 nodeA = cycle[0] nodeB = cycle[1] path = nx.shortest_path(directed_inlining, root, nodeB) if nodeA in path: directed_inlining.remove_edge(nodeB, nodeA) else: directed_inlining.remove_edge(nodeA, nodeB) except nx.NetworkXNoCycle: pass assert len(get_root_nodes(directed_inlining)) == 1 directed_inlinings.append(directed_inlining) return directed_inlinings
def calc_steiner_spanning_tree(crs_projected, input_network_shp, output_network_folder, building_nodes_shp, output_edges, output_nodes, weight_field, type_mat_default, pipe_diameter_default, type_network, total_demand_location, create_plant, allow_looped_networks, optimization_flag, plant_building_names, disconnected_building_names): # read shapefile into networkx format into a directed graph, this is the potential network graph = nx.read_shp(input_network_shp) nodes_graph = nx.read_shp(building_nodes_shp) # tolerance tolerance = 6 # transform to an undirected graph iterator_edges = graph.edges(data=True) # get graph G = nx.Graph() for (x, y, data) in iterator_edges: x = (round(x[0], tolerance), round(x[1], tolerance)) y = (round(y[0], tolerance), round(y[1], tolerance)) G.add_edge(x, y, weight=data[weight_field]) # get nodes iterator_nodes = nodes_graph.nodes(data=False) terminal_nodes = [(round(node[0], tolerance), round(node[1], tolerance)) for node in iterator_nodes] if len(disconnected_building_names) > 0: # identify coordinates of disconnected buildings and remove form terminal nodes list all_building_nodes_df = gdf.from_file(building_nodes_shp) all_building_nodes_df.loc[:, 'coordinates'] = all_building_nodes_df['geometry'].apply( lambda x: (round(x.coords[0][0], tolerance), round(x.coords[0][1], tolerance))) disconnected_building_coordinates = [] for building in disconnected_building_names: # skip disconnected buildings that were filtered out because they had no demand if building in list(all_building_nodes_df['Name']): index = np.where(all_building_nodes_df['Name'] == building)[0] disconnected_building_coordinates.append(all_building_nodes_df['coordinates'].values[index][0]) for disconnected_building in disconnected_building_coordinates: terminal_nodes = [i for i in terminal_nodes if i != disconnected_building] # calculate steiner spanning tree of undirected graph try: mst_non_directed = nx.Graph(steiner_tree(G, terminal_nodes)) except: raise ValueError('There was an error while creating the Steiner tree. ' 'Check the streets.shp for isolated/disconnected streets (lines) and erase them, ' 'the Steiner tree does not support disconnected graphs.') nx.write_shp(mst_non_directed, output_network_folder) # populate fields Building, Type, Name mst_nodes = gdf.from_file(output_nodes) building_nodes_df = gdf.from_file(building_nodes_shp) building_nodes_df.loc[:, 'coordinates'] = building_nodes_df['geometry'].apply( lambda x: (round(x.coords[0][0], tolerance), round(x.coords[0][1], tolerance))) # if there are disconnected buildings if len(disconnected_building_names) > 0: for disconnected_building in disconnected_building_coordinates: building_id = int(np.where(building_nodes_df['coordinates'] == disconnected_building)[0]) building_nodes_df = building_nodes_df.drop(building_nodes_df.index[building_id]) mst_nodes.loc[:, 'coordinates'] = mst_nodes['geometry'].apply( lambda x: (round(x.coords[0][0], tolerance), round(x.coords[0][1], tolerance))) names_temporary = ["NODE" + str(x) for x in mst_nodes['FID']] new_mst_nodes = mst_nodes.merge(building_nodes_df, suffixes=['', '_y'], on="coordinates", how='outer') new_mst_nodes.fillna(value="NONE", inplace=True) new_mst_nodes.loc[:, 'Building'] = new_mst_nodes['Name'] new_mst_nodes.loc[:, 'Name'] = names_temporary new_mst_nodes.loc[:, 'Type'] = new_mst_nodes['Building'].apply(lambda x: 'CONSUMER' if x != "NONE" else x) # populate fields Type_mat, Name, Pipe_Dn mst_edges = gdf.from_file(output_edges) mst_edges.loc[:, 'Type_mat'] = type_mat_default mst_edges.loc[:, 'Pipe_DN'] = pipe_diameter_default mst_edges.loc[:, 'Name'] = ["PIPE" + str(x) for x in mst_edges.index] if allow_looped_networks == True: # add loops to the network by connecting None nodes that exist in the potential network mst_edges, new_mst_nodes = add_loops_to_network(G, mst_non_directed, new_mst_nodes, mst_edges, type_mat_default, pipe_diameter_default) mst_edges.drop(['weight'], inplace=True, axis=1) if create_plant: if optimization_flag == False: building_anchor = calc_coord_anchor(total_demand_location, new_mst_nodes, type_network) new_mst_nodes, mst_edges = add_plant_close_to_anchor(building_anchor, new_mst_nodes, mst_edges, type_mat_default, pipe_diameter_default) else: for building in plant_building_names: building_anchor = building_node_from_name(building, new_mst_nodes) new_mst_nodes, mst_edges = add_plant_close_to_anchor(building_anchor, new_mst_nodes, mst_edges, type_mat_default, pipe_diameter_default) new_mst_nodes.drop(["FID", "coordinates", 'floors_bg', 'floors_ag', 'height_bg', 'height_ag', 'geometry_y'], axis=1, inplace=True) nx.write_shp(mst_non_directed, output_network_folder) # get coordinate system and reproject to UTM mst_edges.crs = crs_projected new_mst_nodes.crs = crs_projected mst_edges.to_file(output_edges, driver='ESRI Shapefile') new_mst_nodes.to_file(output_nodes, driver='ESRI Shapefile')
def solveSteinerTreeDTH(self): print("start node: ", self.start_loc) print("home nodes: ", self.homes) # print("Traversal actual with homes: ", traversal_ordering) leaf_homes = self.getLeafNodes() preorder_nodes = dfs_preorder_nodes(self.steiner_tree, source=self.start_loc) traversal_ordering = [n for n in preorder_nodes if n in leaf_homes] print("Traversal ordering of the leaf: ", traversal_ordering) # """Remove non-leaf nodes from the order.""" # for home in traversal_ordering: # if home not in leaf_homes: # print(home) # traversal_ordering.remove(home) # current_loc = self.start_loc # """ Needs to start and end at root""" # print("Traversal_ordering: ", traversal_ordering) # # traversal_ordering.insert(0, current_loc) # # traversal_ordering.append(current_loc) """Hash map of the dropoffs""" dropoffs = dict() for i in range(len(traversal_ordering) - 1): current_leaf_home = traversal_ordering[i] next_leaf_home = traversal_ordering[i + 1] """Shortest path between current and next leaf home on the graph""" shortest_path = netx.shortest_path(self.netx_graph, source=current_leaf_home, target=next_leaf_home) print("Shortest path between ", current_leaf_home, " and ", next_leaf_home, shortest_path) for node in shortest_path: """Check if any node in the shortest node is a part of the Steiner Tree """ if node != current_leaf_home and node != next_leaf_home and node in self.steiner_tree: #print("Node : ", node) """Shares a common ancestor.""" """Drop off curr_node at curr->parent""" #print("Steiner tree: ", set(self.steiner_tree)) print( "Dfs ", networkx.dfs_predecessors(self.steiner_tree, source=self.start_loc)) dropoffs[current_leaf_home] = networkx.dfs_predecessors( self.steiner_tree, source=self.start_loc)[current_leaf_home] print("Dropoffs ", dropoffs) new_candidate_dropoff = set() for home in self.homes: if home in dropoffs.keys(): new_candidate_dropoff.add(dropoffs[home]) else: new_candidate_dropoff.add(home) print("homes ", self.homes) new_candidate_dropoff = list(new_candidate_dropoff) """Add source to the candidate dropoff list to create the steiner tree.""" new_candidate_dropoff.append(self.start_loc) #print("Candidate_dropoffs ", new_candidate_dropoff) steiner_tree_candidate_dropoffs = steiner_tree(self.netx_graph, new_candidate_dropoff, weight='weight') preorder_nodes = dfs_preorder_nodes(steiner_tree_candidate_dropoffs, source=self.start_loc) preorder_nodes = list(preorder_nodes) final_order = [ n for n in preorder_nodes if n in steiner_tree_candidate_dropoffs ] #print("final order pre", final_order) for elem in final_order: if elem in dropoffs.values(): keys = [k for k, v in dropoffs.items() if v == elem] if elem in self.homes or elem == self.start_loc: for i in keys: final_order.insert( final_order.index(elem) + 1, elem + " " + i) else: index = final_order.index(elem) for i in keys: final_order[index] = elem + " " + i index = index + 1 print("final order ", final_order) return self.get_cost_params(final_order)
def module3(G, topFeatures): sT = steiner_tree(G, topFeatures, weight='weight') return sT
def _compute_links(self, gr, conn_weigths, parts, partitioning_kwargs): total_connect(gr, [b.id for b in self.downloaded_buildings], {b.id: str(b.id) + '_a' for b in self.downloaded_buildings}, connection_type_weigths=conn_weigths, phases=1, type='link', logger=self.logger) street_nodes = [n for n in gr.nodes if gr.nodes[n]['type'] in ('street', 'street_link')] self.logger.debug('Minimum spanning tree calculation...') tc = deepcopy(gr) # clustering self.logger.debug('Cluster elaboration...') valid_nodes = [n for n in gr.nodes if gr.nodes[n]['type'] == 'load'] for n in tc.nodes: if tc.nodes[n]['type'] == 'load': tc.nodes[n]['pwr'] = tc.nodes[n]['building']['area'] * 0.01 else: tc.nodes[n]['pwr'] = 0.01 tot_pwr = sum(nx.get_node_attributes(tc, 'pwr').values()) start_time = time() clusters = partition_grid(tc, [x * tot_pwr for x in parts], 'virtual_length', 'pwr', partitioning_kwargs) end_time = time() self.logger.info(f'Clustering step completed in {end_time - start_time:.2f} seconds') loads_in_clusters = [len([n for n in cluster if gr.nodes[n]['type'] == 'load']) for cluster in clusters] self.logger.debug('{} clusters created of cardinality: {}'.format(len(clusters), loads_in_clusters)) # knowing the clusters, now we make n copies of the original graph and we generate the clustered subgraph # by clipping away what's unneeded subcoms = [] for clno, cluster in enumerate(clusters): self.logger.debug('Subgraph #{} creation and clipping...'.format(clno)) gr_part = deepcopy(tc) relabel = {x: str(x) + '_' + str(clno) for x in street_nodes} # each cluster must have its own street nodes, because it's possible that the street paths overlap partially nx.relabel_nodes(gr_part, relabel, copy=False) # calculating best traf position no = deepcopy(gr_part.nodes) realnodes = [n for n in no if gr_part.nodes[n]['type'] == 'load' and n in cluster] gro_part = nxs.steiner_tree(gr_part, realnodes) barycenter = nx.barycenter(gro_part, weight='virtual_length')[0] gr_part.add_edge('trmain' + str(clno), barycenter, phases=3, type='entry') gr_part.nodes['trmain' + str(clno)]['type'] = 'source' gr_part.nodes['trmain' + str(clno)]['pos'] = [x+0.000005 for x in gr_part.nodes[barycenter]['pos']] # removing loads not pertaining to this cluster (refines input) gr_part.remove_nodes_from([n for n in no if gr_part.nodes[n]['type'] == 'load' and n not in cluster]) # steiner # gr_part = nxs.steiner_tree(gr_part, realnodes, weight='virtual_length') # mst gr_part = nx.minimum_spanning_tree(gr_part, weight='virtual_length') # remove street nodes that are not used by this cluster sps = nx.shortest_path(gr_part, 'trmain' + str(clno), weight='virtual_length') sps = {k: v for k, v in sps.items() if k in valid_nodes and k in cluster} used_nodes = set() for target, path in sps.items(): if gr_part.nodes[target]['type'] in ('street', 'street_link'): raise ValueError else: used_nodes.update(set(path)) unused_nodes = set(gr_part.nodes) - used_nodes gr_part.remove_nodes_from(unused_nodes) assert nx.number_connected_components(gr_part) <= 1 assert nx.is_tree(gr_part) subcoms.append(gr_part) # union of the subgraphs try: self.g = nx.union_all(subcoms) except nx.NetworkXError: from itertools import combinations ls = [(n, set(x.nodes)) for n, x in enumerate(subcoms)] for n1x, n2y in combinations(ls, 2): n1, x = n1x n2, y = n2y print('{},{} : {}'.format(n1, n2, x.intersection(y))) raise self.logger.info('Grid created')
def main(Population_size, N_GEN, Mutation_prob, Crossover_prob): Rad = 30 ###communication radius of a node g = 600 ###cardinality of total sensors in the network B_SR = 100 ###bandwidth between sensor and relay S_Per_R = 600 ###total sensors per relay Relay_constraint = 25 ###total relays to be deployed Sensor_list = [] ###list of sensors Relay_list = [] ###list of relays N_R_R_Diction = {} ###dictionary to show relay-relay connectivity N_S_R_Diction = {} ###dictionary to show sensor-relay connectivity width = 100.0 ###width of the area on which sensors and relays are to be deployed for i in range(g): ###generating sensor list Sensor_list.append((round(random.uniform(0.0,width),6),round(random.uniform(0.0,width),6))) row = 0 col = 0 while row <= width: ###generating relay list while col <= width: Relay_list.append((float(row),float(col))) col += 10 row += 10 col = 0 ###print("Sensor List") ###print(Sensor_list) ###print("Relay List") ###print(Relay_list) def distance(a,b): ###calculates the Euclidean distance return math.sqrt((a**2) + (b**2)) def Connection_s_r(SN,RN): ###calculates the connectivity matrix for sensor x relay layer Neighbor_Sensor_Relay = [[[0] for i in range(len(RN))] for j in range(len(SN))] for i in range(len(SN)): for j in range(len(RN)): dist = distance(abs(SN[i][0] - RN[j][0]), abs(SN[i][1]-RN[j][1])) if dist <= Rad: if i in N_S_R_Diction: N_S_R_Diction[i].append(j) else: N_S_R_Diction[i] = [j] Neighbor_Sensor_Relay[i][j] = 1 elif dist > Rad: Neighbor_Sensor_Relay[i][j] = 0 return Neighbor_Sensor_Relay N_S_R = (Connection_s_r(Sensor_list,Relay_list)) ###print('Neighbor Matrix of Sensor x Relay Layer', '\n') ###for i in n_s_r: ###print(i) def Connection_r_r(RN): ###calculates the Connectivity matrix for relay x relay layer Neighbor_Relay_Relay = [[[0] for i in range(len(RN))] for j in range(len(RN))] for i in range(len(RN)): for j in range(len(RN)): dist = distance(abs(RN[i][0] - RN[j][0]), abs(RN[i][1] - RN[j][1])) if i == j: Neighbor_Relay_Relay[i][j] = 0 elif dist <= Rad: if i in N_R_R_Diction: N_R_R_Diction[i].append(j) else: N_R_R_Diction[i] = [j] Neighbor_Relay_Relay[i][j] = 1 elif dist > Rad: Neighbor_Relay_Relay[i][j] = 0 return Neighbor_Relay_Relay N_R_R = (Connection_r_r(Relay_list)) ###print('Neighbor Matrix of Relay x Relay Layer', '\n') ###for i in n_r_r: ###print(i) def Link_s_r(SN,RN): ###calculates the Data link flow matrix for sensor x relay layer bandwidth = 100.0 Linkflow_Sensor_Relay = [[[0] for i in range(len(RN))] for j in range(len(SN))] for i in range(len(SN)): for j in range(len(RN)): Linkflow_Sensor_Relay[i][j] = bandwidth return Linkflow_Sensor_Relay l_s_r = (Link_s_r(Sensor_list,Relay_list)) ###print('Data Link Flow Matrix of Sensor x Relay Layer', '\n') ###for i in l_s_r: ###print(i) def Link_r_r(RN): ###calculates the Data link flow matrix for relay x relay layer bandwidth = 200.0 Linkflow_Relay_Relay = [[[0] for i in range(len(RN))] for j in range(len(RN))] for i in range(len(RN)): for j in range(len(RN)): if i != j: Linkflow_Relay_Relay[i][j] = bandwidth else: Linkflow_Relay_Relay[i][j] = 0 return Linkflow_Relay_Relay l_r_r = (Link_r_r(Relay_list)) ###print('Data Link Flow Matrix of Relay x Relay Layer', '\n') ###for i in l_r_r: ###print(i) def Energy_s_r(SN,RN): ###calculates the Energy matrix for sensor x relay layer bandwidth = 100.0 Energy_Sensor_Relay = [[[0] for i in range(len(RN))] for j in range(len(SN))] E_radio_S = 50.0 * (10 ** (-9)) E_radio_R = 100.0 * (10 ** (-9)) Transmit_amplifier = 100.0 * (10 ** (-12)) for i in range(len(SN)): for j in range(len(RN)): dist = distance(abs(SN[i][0] - RN[j][0]), abs(SN[i][1] - RN[j][1])) energy_sensor_tx = float(bandwidth * (E_radio_S + (Transmit_amplifier * (dist **2)))) ###energy used when sensor transmits data energy_relay_rx = float(bandwidth * E_radio_R) ###energy used when relay receives data total_energy = energy_sensor_tx + energy_relay_rx Energy_Sensor_Relay[i][j] = total_energy/4 ###Only 1/4 sensors will be active per time return Energy_Sensor_Relay ###unit for every relay e_s_r = (Energy_s_r(Sensor_list,Relay_list)) ###print('Energy Matrix of Sensor x Relay layer', '\n') ###for i in e_s_r: ###print(i) def Energy_r_r(RN): ###calculates the Energy matrix for relay x relay layer bandwidth = 200.0 Energy_Relay_Relay = [[[0] for i in range(len(RN))] for j in range(len(RN))] E_radio_R = 100.0 * (10 ** (-9)) Transmit_amplifier = 100.0 * (10 ** (-12)) for i in range(len(RN)): for j in range(len(RN)): dist = distance(abs(RN[i][0] - RN[j][0]), abs(RN[i][1] - RN[j][1])) energy_relay_tx = float(bandwidth * (E_radio_R + (Transmit_amplifier * (dist**2)))) ###energy used when relay transmits data energy_relay_rx = float(bandwidth * E_radio_R) ###energy used when relay receives data total_energy = (energy_relay_tx + energy_relay_rx) Energy_Relay_Relay[i][j] = total_energy return Energy_Relay_Relay e_r_r = (Energy_r_r(Relay_list)) ###print('Energy Matrix of Relay x Relay layer', '\n') ###for i in e_r_r: ###print(i) def RelaysDiction(Relay_list): ###forms the relay graph connectivity its connected edges G = nx.Graph() for i in range(len(Relay_list)): G.add_node(i) for j in range(len(Relay_list)): if abs(distance(Relay_list[i][0] - Relay_list[j][0],Relay_list[i][1] - Relay_list[j][1])) <= Rad: G.add_edge(i,j) G.add_edge(j,i) return G RELAYS_GRAPH = RelaysDiction(Relay_list) def ProduceOffSprings(): ''' this function is used to generate a single individual which contains our system properties i.e. sensor-relay and relay-relay characteristics ''' individual = [] ###list for an offsprings characteristics SR_relation = [] ###list for the sensr-relay relation of an offsprings characteristics RR_relation = [] ###list for the relay-relay relation of an offsprings characteristics relay_record = set() ###a list to maintain the record of activated relays in the sensr-relay relation ###of an offsprings characteristics for i in range(len(Sensor_list)): ###generating sensor-relay characteristics for an individual lst = [0 for i in range(len(Relay_list))] rand = random.choice(N_S_R_Diction[i]) if (rand) not in relay_record: relay_record.add(rand) lst[rand] = 1 SR_relation.append(lst) for i in range(len(Relay_list)): ###generating relay-relay characteristics for an individual lst = [0 for i in range(len(Relay_list))] RR_relation.append(lst) for i in relay_record: for j in relay_record: if i in N_R_R_Diction[j] and i!=j: RR_relation[i][j] = 1 ###print("SR part of the Individual") ###for j in SR_relation: ###print(j) ###print("RR part of the Individual") ###for j in RR_relation: ###print(j) individual.append(SR_relation) individual.append(RR_relation) return individual def criteria(lst): ''' a function that checks whether the given individual avoids the hotspot problem and satisfies the relay constraint ''' total_linkflow = 0 ###variable showing the total linkflow relays_deployed = set() lst_SR = lst[0] lst_RR = lst[1] for i in range(len(Relay_list)): for j in range(len(Sensor_list)): if lst_SR[j][i] == 1: total_linkflow += l_s_r[j][i] relays_deployed.add(i) if total_linkflow > B_SR * S_Per_R: return False total_linkflow=0 if len(relays_deployed) > Relay_constraint: return False return True def Max_sensors_per_relay(lst): ''' this function tell us the maximum number of sensor per relay. ''' total_linkflow = 0 ###variable showing the total linkflow relays_deployed = set() lst_SR = lst[0] lst_RR = lst[1] linkflow_list_record = [] for i in range(len(Relay_list)): for j in range(len(Sensor_list)): if lst_SR[j][i] == 1: total_linkflow += l_s_r[j][i] linkflow_list_record.append(total_linkflow) if total_linkflow > B_SR * S_Per_R: return False break total_linkflow = 0 max_linkflow = max(linkflow_list_record) relay_number = linkflow_list_record.index(max_linkflow) print("Maximum number of sensors per relay") print("Relay Number : ", relay_number, ", Max Linkflow : ",max_linkflow ,", Sensors connected : ",max_linkflow/B_SR) def evaluate(individual): ''' this function checks whether the individual matches the criteria and if yes, then it calculates the total energy consumed between sensor-relay and relay-relay. ''' ga_energy = 0 lst_SR = individual[0] lst_RR = individual[1] if not criteria(individual): ###condition to check if a individual meets the criteria return float('inf') else: diction = {} for i in range(len(lst_SR)): ###calculates the energy between sensor-relay communication for j in range(len(lst_SR[0])): if lst_SR[i][j] == 1 and N_S_R[i][j] == 1: ga_energy += e_s_r[i][j] for i in range(len(lst_RR)): ###calculates the energy between relay-relay communication for j in range(len(lst_RR[0])): if lst_RR[i][j] == 1 and N_R_R[i][j] ==1: ga_energy += e_r_r[i][j] return ga_energy def crossover(individual_1,individual_2): ''' the purpose of this function is to crossover between two lists i.e. two individuals maintaining the pattern produced during offspring production ''' rand = random.randint(1,len(individual_1[0])-1) relay_record_1 = set() relay_record_2 = set() lst_SR_1 = individual_1[0] lst_SR_2 = individual_2[0] temp_RR_1 = [] temp_RR_2 = [] temp_SR_1 = copy.deepcopy(lst_SR_1[0:rand] + lst_SR_2[rand:]) ###Cross over the sensor-relay part of the individual temp_SR_2 = copy.deepcopy(lst_SR_2[0:rand] + lst_SR_1[rand:]) for i in range(len(temp_SR_1)): ###generating the relay-relay part of the individual for j in range(len(temp_SR_1[0])): if temp_SR_1[i][j] == 1: relay_record_1.add(j) for i in range(len(Relay_list)): lst = [0 for i in range(len(Relay_list))] temp_RR_1.append(lst) for i in relay_record_1: for j in relay_record_1: if i in N_R_R_Diction[j] and i!=j: temp_RR_1[i][j] = 1 for i in range(len(temp_SR_2)): for j in range(len(temp_SR_2[0])): if temp_SR_2[i][j] == 1: relay_record_2.add(j) for i in range(len(Relay_list)): lst = [0 for i in range(len(Relay_list))] temp_RR_2.append(lst) for i in relay_record_2: for j in relay_record_2: if i in N_R_R_Diction[j] and i!=j: temp_RR_2[i][j] = 1 offspring_1 = [] offspring_2 = [] offspring_1.append(temp_SR_1) offspring_1.append(temp_RR_1) offspring_2.append(temp_SR_2) offspring_2.append(temp_RR_2) return offspring_1, offspring_2 def mutate(lst): ''' this function mutates i.e. changes the position of 1 in the given individual maintaining the pattern produced during offspring production ''' set_of_relays = set() Mutated_lst = [] lst_SR = lst[0] lst_RR = lst[1] relay_record = set() breaked = False for i in range(len(lst_SR)): ###mutating the sensor-relay part of the individual breaked = False for j in N_S_R_Diction[i]: if j in set_of_relays: lst_SR[i][lst_SR[i].index(1)], lst_SR[i][j] = 0,1 breaked = True break if not breaked: rand = random.choice(N_S_R_Diction[i]) lst_SR[i][lst_SR[i].index(1)], lst_SR[i][rand] = 0,1 set_of_relays.add(rand) for i in range(len(lst_SR)): ###generating the relay-relay part of the individual for j in range(len(lst_SR[0])): if lst_SR[i][j] == 1: relay_record.add(j) for i in range(len(Relay_list)): lst = [0 for i in range(len(Relay_list))] lst_RR.append(lst) for i in relay_record: for j in relay_record: if i in N_R_R_Diction[j] and i!=j: lst_RR[i][j] = 1 Mutated_lst.append(lst_SR) Mutated_lst.append(lst_RR) return Mutated_lst def genetic_algorithm(): ''' this is our primary function which essentially solves our minimum energy consumption problem on UWSNs working on sleep scheduling routing protocols. ''' population = [ProduceOffSprings() for i in range(Population_size)] ###creating individuals for the size of population vals = [evaluate(i) for i in population] ###evaluating each individual in the population for i in range(N_GEN): vals, population = (zip(*sorted(zip(vals, population)))) vals = list(vals) population = list(population) for j in range(6): if random.random() < Crossover_prob: population[-j], population[-(j+1)] = crossover(population[j], population[j+1]) vals[-j] = evaluate(population[-j]) vals[-(j+1)] = evaluate(population[-(j+1)]) for j in range(6,len(population)-5): if random.random() < Mutation_prob: population[j] = mutate(population[j]) vals[j] = evaluate(population[j]) vals, population = (zip(*sorted(zip(vals, population)))) vals = list(vals) population = list(population) return population, vals population, vals = genetic_algorithm() relays_deployed = set() for i in population: ###print("Fittest Individual") ###print("SR part of the Individual") ###for j in range(len(i[0])): ###print(j," : ", i[0][j]) ###print("RR part of the Individual") ###for j in range(len(i[1])): ###print(j," : ", i[1][j]) ###print("Best Individual") Max_sensors_per_relay(i) sett = set() for j in range(len(i[0])): sett.add(i[0][j].index(1)) x = steiner_tree(RELAYS_GRAPH, sett) break gamma = vals[0] Y = 200.0 E_cosnum_radio_elec_R = 100.0 * 10 ** (-9) transmit_amplifier_R = 100.0 * 10 ** (-12) for i in range(len(x.nodes) - len(sett)): ###adding the approximate energy of steiner nodes in the network relay_receive_data_rate = Y dist = Rad energy_relay = (relay_receive_data_rate * E_cosnum_radio_elec_R) + relay_receive_data_rate * (E_cosnum_radio_elec_R+transmit_amplifier_R * (dist ** 2)) gamma += energy_relay min_energy = gamma print("Maximum relay constraint on the network: ", Relay_constraint) print("Maximum candidate location for relays: ", len(Relay_list)) print("Sensors deployed in the network: ", len(Sensor_list)) print("Radius of communication of each node: ", Rad) print("Relays deployed before steiner nodes: ", len(sett)) print("Relays deployed after steiner nodes: ", len(x.nodes)) return (len(x.nodes), min_energy)
def Optimize(rad, sen): width = 100.0 ##100 by 100 meters of square field R = float(rad) ##Radius of communication of each node relayConstraint = 121 ##Total relays to be deployed sensorList = [] relayList = [] ##Populating sensorList and relayList for i in range(sen): sensorList.append((round(random.uniform(0.0,width),6),round(random.uniform(0.0,width),6))) # print(sensorList) row = 0 col = 0 while row <= width: while col <= width: relayList.append((float(row),float(col))) col += 10 row += 10 col = 0 ##Calculates the Euclidean distance def distance(a,b): return math.sqrt((a**2) + (b**2)) ##Calculates the Connectivity matrix for sensor x relay layer def Connection_s_r(SN,RN): Neighbor_Sensor_Relay = [[[0] for i in range(len(RN))] for j in range(len(SN))] for i in range(len(SN)): for j in range(len(RN)): dist = distance(abs(SN[i][0] - RN[j][0]), abs(SN[i][1]-RN[j][1])) if dist <= R: Neighbor_Sensor_Relay[i][j] = 1 elif dist > R: Neighbor_Sensor_Relay[i][j] = 0 return Neighbor_Sensor_Relay n_s_r = (Connection_s_r(sensorList,relayList)) ###print('Neighbor Matrix of Sensor x Relay Layer', '\n') ###for i in n_s_r: ### print(i) ##Calculates the Connectivity matrix for relay x relay layer def Connection_r_r(RN): Neighbor_Relay_Relay = [[[0] for i in range(len(RN))] for j in range(len(RN))] for i in range(len(RN)): for j in range(len(RN)): dist = distance(abs(RN[i][0] - RN[j][0]), abs(RN[i][1] - RN[j][1])) if i == j: Neighbor_Relay_Relay[i][j] = 0 elif dist <= R: Neighbor_Relay_Relay[i][j] = 1 elif dist > R: Neighbor_Relay_Relay[i][j] = 0 return Neighbor_Relay_Relay n_r_r = (Connection_r_r(relayList)) ###print('Neighbor Matrix of Relay x Relay Layer', '\n') ###for i in n_r_r: ### print(i) ##Calculates the Data link flow matrix for sensor x relay layer def Link_s_r(SN,RN): bandwidth = 100.0 Linkflow_Sensor_Relay = [[[0] for i in range(len(RN))] for j in range(len(SN))] for i in range(len(SN)): for j in range(len(RN)): Linkflow_Sensor_Relay[i][j] = bandwidth return Linkflow_Sensor_Relay l_s_r = (Link_s_r(sensorList,relayList)) ###print('Data Link Flow Matrix of Sensor x Relay Layer', '\n') ###for i in l_s_r: ### print(i) ##Calculates the Data link flow matrix for relay x relay layer def Link_r_r(RN): bandwidth = 200.0 Linkflow_Relay_Relay = [[[0] for i in range(len(RN))] for j in range(len(RN))] for i in range(len(RN)): for j in range(len(RN)): if i != j: Linkflow_Relay_Relay[i][j] = bandwidth else: Linkflow_Relay_Relay[i][j] = 0 return Linkflow_Relay_Relay l_r_r = (Link_r_r(relayList)) ###print('Data Link Flow Matrix of Relay x Relay Layer', '\n') ###for i in l_r_r: ### print(i) ##Calculates the Energy matrix for sensor x relay layer def Energy_s_r(SN,RN): bandwidth = 100.0 Energy_Sensor_Relay = [[[0] for i in range(len(RN))] for j in range(len(SN))] E_radio_S = 50.0 * (10 ** (-9)) E_radio_R = 100.0 * (10 ** (-9)) Transmit_amplifier = 100.0 * (10 ** (-12)) for i in range(len(SN)): for j in range(len(RN)): dist = distance(abs(SN[i][0] - RN[j][0]), abs(SN[i][1] - RN[j][1])) energy_sensor_tx = float(bandwidth * (E_radio_S + (Transmit_amplifier * (dist **2)))) ##energy used when sensor transmits data energy_relay_rx = float(bandwidth * E_radio_R) ##energy used when relay receives data total_energy = energy_sensor_tx + energy_relay_rx Energy_Sensor_Relay[i][j] = total_energy ##Only 1/4 sensors will be active per time unit for every relay return Energy_Sensor_Relay e_s_r = (Energy_s_r(sensorList,relayList)) # print('Energy Matrix of Sensor x Relay layer', '\n') # for i in e_s_r: # print(i) ##Calculates the Energy matrix for relay x relay layer def Energy_r_r(RN): bandwidth = 200.0 Energy_Relay_Relay = [[[0] for i in range(len(RN))] for j in range(len(RN))] E_radio_R = 100.0 * (10 ** (-9)) Transmit_amplifier = 100.0 * (10 ** (-12)) for i in range(len(RN)): for j in range(len(RN)): dist = distance(abs(RN[i][0] - RN[j][0]), abs(RN[i][1] - RN[j][1])) energy_relay_tx = float(bandwidth * (E_radio_R + (Transmit_amplifier * (dist**2)))) ##energy used when relay transmits data energy_relay_rx = float(bandwidth * E_radio_R) ##energy used when relay receives data total_energy = (energy_relay_tx + energy_relay_rx) Energy_Relay_Relay[i][j] = total_energy return Energy_Relay_Relay e_r_r = (Energy_r_r(relayList)) ###print('Energy Matrix of Relay x Relay layer', '\n') ###for i in e_r_r: ### print(i) Problem = LpProblem("The Energy Problem", LpMinimize) Var_S_R = [pulp.LpVariable(str('SR' + str(i) + "_" + str(j)), 0, 1, pulp.LpInteger) for i in range(len(sensorList)) for j in range(len(relayList))] Var_R_R = [pulp.LpVariable(str('RR' + str(i) + "_" + str(j)), 0, 1, pulp.LpInteger) for i in range(len(relayList)) for j in range(len(relayList))] Bool_Var = [pulp.LpVariable(str('B' + str(i)), 0, 1, pulp.LpInteger) for i in range(len(relayList))] k = 0 ##for variable indexing objective = [] ##for objective function linkflow_column = [] ##for data rate constraint conn_SR = [] ##for connection constraint conn_RR = [] while k < len(Var_S_R): for i in range(len(e_s_r)): for j in range(len(e_s_r[i])): objective.append(e_s_r[i][j] * Var_S_R[k]) conn_SR.append(n_s_r[i][j] * Var_S_R[k]) k += 1 Problem += lpSum(conn_SR) == 1 conn_SR = [] B_Max = 3000 ##by controlling the maximum data rate we control number of sensors per relay B_SR = 100 S_Per_R = B_Max / B_SR ##contains the number of sensor per relay constraint applied above (a relay can have a 100 data rate with a sensor) Var_alias_SR = [] for i in Var_S_R: Var_alias_SR.append(str(i)) booleansumcolumn=[] ##holds the variables for each column at a time for i in range(len(relayList)): for j in range(len(sensorList)): e = Var_alias_SR.index(str('SR' + str(j) + "_" + str(i))) ##iterating column wise on the SxR matrix booleansumcolumn.append(Var_S_R[e]) linkflow_column.append(l_s_r[j][i] * Var_S_R[e]) Problem += lpSum(booleansumcolumn) >= Bool_Var[i] ##1. OR gate for constraining number of relays Problem += lpSum(linkflow_column) <= (B_Max) ##controlling number of sensors per relay by constraining the total data rate received by an individual relay for c in booleansumcolumn: Problem += c <= Bool_Var[i] ##2. OR gate for constraining number of relays booleansumcolumn=[] ##emptying the columns to fill the variables of the next column linkflow_column=[] k = 0 while k < len(Var_R_R): for i in range(len(e_r_r)): for j in range(len(e_r_r[i])): objective.append(e_r_r[i][j] * Var_R_R[k]) conn_RR.append(n_r_r[i][j] * Var_R_R[k]) k += 1 Problem += lpSum(conn_RR) >= 0 conn_RR = [] Var_alias_RR = [] for i in Var_R_R: Var_alias_RR.append(str(i)) booleansumcolumn=[] ##holds the variables for each column at a time for i in range(len(relayList)): for j in range(len(relayList)): e = Var_alias_RR.index(str('RR' + str(j) + "_" + str(i))) ##iterating column wise on the RxR matrix booleansumcolumn.append(Var_R_R[e]) Problem += lpSum(booleansumcolumn) >= Bool_Var[i] ##1. OR gate for constraining number of relays for c in booleansumcolumn: Problem += c <= Bool_Var[i] ##2. OR gate for constraining number of relays booleansumcolumn=[] ##emptying the columns to fill the variables of the next column for i in range(len(relayList)): ##making sure that the relay x relay matrix is symmetric for j in range(len(relayList)): if i != j: e = Var_alias_RR.index(str('RR' + str(j) + "_" + str(i))) f = Var_alias_RR.index(str('RR' + str(i) + "_" + str(j))) Problem += Var_R_R[e] == Var_R_R[f] elif i == j: ##controversial condition f = Var_alias_RR.index(str('RR' + str(i) + "_" + str(j))) Problem += Var_R_R[f] == 0 Problem += lpSum(objective) ##adding the objective energy linear combination to minimize Problem += lpSum(Bool_Var) <= relayConstraint ##number of relays constraint finally ###print(Problem) t1=time.clock() Problem.solve(solvers.PULP_CBC_CMD(fracGap=0.0000000000001)) ##solving the problem t2=time.clock() ##this part (below) is just to print and understand how the code works DeployedRelays_SR = [] DeployedRelays_RR = [] for v in Problem.variables(): ###print(v.name, "=", v.varValue) if v.varValue == 1.0 and v.name in Var_alias_SR: DeployedRelays_SR.append(v.name) elif v.varValue == 1.0 and v.name in Var_alias_RR: DeployedRelays_RR.append(v.name) Fin_Conn_S_R = [[0 for i in range(len(relayList))] for j in range(len(sensorList))] ##the output matrix produced by the LP solver Fin_Conn_R_R = [[0 for i in range(len(relayList))] for j in range(len(relayList))] b = 0 ##used to keep track of the index number of the string for i in DeployedRelays_SR: for j in i: if j !="_": b += 1 else: break s = int(i[2:b]) ##stores the indexes of SxR matrix that are 1(high) r = int(i[b+1:]) Fin_Conn_S_R[s][r] = 1 b = 0 # print('Final Optimized Connectivity Matrix of Sensor x Relay layer: ', '\n') # for i in Fin_Conn_S_R: # print(i) b = 0 ##used to keep track of the index number of the string for i in DeployedRelays_RR: for j in i: if j !="_": b += 1 else: break s = int(i[2:b]) ##stores the indexes of RxR matrix that are 1(high) r = int(i[b+1:]) Fin_Conn_R_R[s][r] = 1 b = 0 ##print('Final Optimized Connectivity Matrix of Relay x Relay layer: ', '\n') ##for i in Fin_Conn_R_R: ## print(i) v = [] connection={} for i in range(len(Fin_Conn_S_R)): for j in range(len(Fin_Conn_S_R[i])): if Fin_Conn_S_R[i][j] == 1 and j not in v: connection['Relay' + str(j)] = ['Sensor' + str(i)] v.append(j) elif Fin_Conn_S_R[i][j] == 0: pass else: connection['Relay' + str(j)].append('Sensor' + str(i)) ##this part (above) is just to print and understand how the code works print("Optimum Relays Used: ") print(sorted(connection),"\n") print("The Relay-Sensor dictionary: \n") print(connection, "\n") print ("Status:", LpStatus[Problem.status]) print("Number of Candidate Location for Relays: ", len(relayList)) print("Number of Sensors in the Network: ", len(sensorList)) print('Number Of Relays Deployed on the Candidate Locations: ', len(connection)) print("Communication Radius of the Each Node: ", R) print("Number of Base stations: ", 1) print("Time Taken to Calculate energy: ", t2-t1) gamma = value(Problem.objective) ##total optimum energy without steiner problem # print("Energy of the network before steiner node deployment: ", value(Problem.objective)) ##now checking if the assumption was correct or do we need to add more relay nodes for a single path? (steiner tree problem) def relayNumtoInt(string): i = 0 while not string[i].isdigit(): i += 1 return int(string[i:])-1 connection_list = sorted(connection) terminal_nodes = ([relayNumtoInt(connection_list[i]) for i in range(len(connection_list))]) diction = {i: [j for j, adjacent in enumerate(row) if adjacent] for i, row in enumerate(n_r_r)} G = nx.Graph(diction) x = steiner_tree(G, terminal_nodes) ##returns all the nodes that form a tree if len(x.nodes) == len(terminal_nodes): print('No stiener nodes required.') else: ##if additional nodes are deployed we add their energies too B_R = 200.0 ##taking approximately largest distance dist = R E_radio_R = 100.0 * (10**(-9)) Transmit_amplifier = 100.0 * (10**(-12)) energy_relay = (B_R * E_radio_R) + B_R * (E_radio_R + Transmit_amplifier * (dist ** 2)) for i in range(len((set(x.nodes) - set(terminal_nodes)))): gamma += (energy_relay) ##adding relay energy of steiner nodes ##print('Additional steiner nodes deployed ', (set(x.nodes) - set(terminal_nodes))) a = 0 for i in connection: ##calculating maximum sensor per relay a = max(a,len(connection[i])) ## creating a separate Relay-Relay matrix for additional relays deployed by Steiner node's algorithm steiner_R_R = [[0 for j in range(len(Fin_Conn_R_R))] for i in range(len(Fin_Conn_R_R[0]))] steiner_dict = nx.to_dict_of_dicts(x) for relay_ in steiner_dict.keys(): neighbors_ = steiner_dict[relay_].keys() print(relay_, steiner_dict[relay_].keys()) ##traverse through all neighbors: for i in neighbors_: ##add these relay connections to steiner_R_R matrix if only its not present in Fin_Conn_R_R if(Fin_Conn_R_R[relay_][i]==0 and steiner_R_R[i][relay_]!=1): steiner_R_R[relay_][i]=1 ## print("Maximum Sensors per Relay in the Network: ", a) ## print("Maximum Sensor per Relay constraint of the Network: " , S_Per_R) ## print("Maximum Relay constraint of the Network: ", relayConstraint) ## print("Total Energy of the network after steiner nodes: ", gamma) ## print("Total Number of relays deployed after steiner nodes: ", len(x.nodes)) def Energy_s(SN,RN): ### creates a matrix for sensor energy only bandwidth = 100.0 Energy_Sensor = [[[0] for i in range(len(RN))] for j in range(len(SN))] E_radio_S = 50.0 * (10 ** (-9)) # E_radio_R = 100.0 * (10 ** (-9)) Transmit_amplifier = 100.0 * (10 ** (-12)) for i in range(len(SN)): for j in range(len(RN)): dist = distance(abs(SN[i][0] - RN[j][0]), abs(SN[i][1] - RN[j][1])) energy_sensor_tx = float(bandwidth * (E_radio_S + (Transmit_amplifier * (dist **2)))) ##energy used when sensor transmits data total_energy = energy_sensor_tx Energy_Sensor[i][j] = total_energy ##Only 1/4 sensors will be active per time unit for every relay return Energy_Sensor e_s = Energy_s(sensorList, relayList) #creates a matrix of relay energy used in 1 round def Energy_r(RN): bandwidth_r = 200.0 bandwidth_s = 100.0 Energy_Relay_Relay = [[(0, 0) for i in range(len(RN))] for j in range(len(RN))] E_radio_R = 100.0 * (10 ** (-9)) Transmit_amplifier = 100.0 * (10 ** (-12)) E_aggregation = 0.00001 ##aggregation energy for i in range(len(RN)): for j in range(len(RN)): dist = distance(abs(RN[i][0] - RN[j][0]), abs(RN[i][1] - RN[j][1])) energy_relay_tx = float(bandwidth_r * (E_radio_R + (Transmit_amplifier * (dist**2)))) ##energy used when relay transmits data energy_relay_rx = float(bandwidth_r * E_radio_R) ##energy used when relay receives data energy_relay_rx_s = float(bandwidth_s* E_radio_R) total_energy = (energy_relay_tx + energy_relay_rx) Energy_Relay_Relay[i][j] = (total_energy, energy_relay_rx_s, E_aggregation) return Energy_Relay_Relay e_r = Energy_r(relayList) def init_Energy_r(): ### creates a matrix for the simulation to use (stores the energy of relays) init_e_r = 0.005; nw_e_r = [[0 for j in range(len(relayList))] for i in range(len(relayList))] for i in range(len(relayList)): for j in range(len(relayList)): if(Fin_Conn_R_R[i][j]==1 or steiner_R_R[i][j]==1): nw_e_r[i][j]=init_e_r return nw_e_r nw_e_r = init_Energy_r() def init_Energy_s(): init_e_s = 0.0005 nw_e_s = [[0 for j in range(len(relayList))] for i in range(len(sensorList))] for i in range(len(sensorList)): for j in range(len(relayList)): if(Fin_Conn_S_R[i][j]==1): nw_e_s[i][j]=init_e_s return nw_e_s nw_e_s = init_Energy_s() #returns a matrix which contains the states of all the deployed sensors in the network def state_matrix(SN, RN, Fin_Conn_S_R): state_s = [[0 for j in range(len(RN))] for i in range(len(SN))] for i in range(len(SN)): for j in range(len(RN)): if(Fin_Conn_S_R[i][j]==1): state_s[i][j] = 1 return state_s state_s = state_matrix(sensorList, relayList, Fin_Conn_S_R) def init_s_counter(state_s, Fin_conn_S_R): #count all active sensors connected to a relay #counter variable: contains a list of the format: [counter, starting_index_of1/4th_sensors,index_of_sensor1, index_of_sensor2,...] s_counter = [[0, 2] for i in range(len(state_s[0]))] for i in range(len(state_s[0])): for j in range(len(state_s)): if(Fin_conn_S_R[j][i]==1): s_counter[i][0]+=1 s_counter[i].append(j) #stores the row index of the sensor # ##divide the counted values into 4 parts # for i in range(len(s_counter)): # if((s_counter[i][0]//4 )!=0): ##if s_counter divided by 4 gives 0, then take all sensonrs in one round only # s_counter[i][0] = s_counter[i][0]//4 return s_counter s_counter = init_s_counter(state_s, Fin_Conn_S_R) # print("s_counter: ") # for i in s_counter: # print(i) def init_sensor_PH_value(SN): ###initializes the PH values for all sensor nodes PH_list_s = [7.2 for j in range(len(SN))] return PH_list_s PH_list_sensors = init_sensor_PH_value(sensorList) def Master_PH_checker(PH_list_sensors, Fin_Conn_S_R, state_s): ###checks the pH of all sensor nodes. Clusters_with_oil_spills = [] ##contains all relay indexes where oil spill was detected for i in range(len(Fin_Conn_S_R[0])): aggregated_PH = 0 no_of_sensors = 0 for j in range(len(Fin_Conn_S_R)): pH_value = PH_list_sensors[j] ##ph of the sensor # print(pH_value) if (Fin_Conn_S_R[j][i]==1 and state_s[j][i] ==1): ##check if the pH is not normal and the relay is connected to the sensor and the sensor is turned on aggregated_PH+= pH_value no_of_sensors+=1 if(no_of_sensors!=0): aggregated_PH = float(aggregated_PH)/float(no_of_sensors) if(aggregated_PH>8.5):##if aggregated_PH is greater than 8.5 than an oil spill is detected Clusters_with_oil_spills.append(i) return Clusters_with_oil_spills def PH_checker(PH_list_sensors, Fin_Conn_S_R, state_s, cluster_head): aggregated_PH = 0 no_of_sensors = 0 for i in range(len(PH_list_sensors)): if(Fin_Conn_S_R[i][cluster_head]==1 and state_s[i][cluster_head]==1): ##check if sensor and relay are connected and switched on and pH level is exceded aggregated_PH+=PH_list_sensors[i] ##if spill detected if(no_of_sensors!=0): aggregated_PH = aggregated_PH/no_of_sensors if(aggregated_PH>8.5): return True else: return False else: return False def add_neighbour(Cluster_head, Fin_Conn_R_R, checked): ##cluster_head contains the index of that relay which cluster detects oil leaks ##checks and returns neigbouring clusters to a cluster neighbor_relays = [] for i in range(len(Fin_Conn_R_R)): #checking for the relays connected to the "Cluster_head" relay possible_neighbor = Fin_Conn_R_R[i][Cluster_head] if(possible_neighbor==1 and (possible_neighbor not in checked)): ##if there is a 1, then the relays are neighbors! neighbor_relays.append(i) return neighbor_relays def oil_simulator(Fin_Conn_S_R, sensorList, PH_list_sensors): xrange = 50 yrange = 50 oil_spill_PH = 10 ## pH value of oil for i in range(len(sensorList)): # print(sensorList[i]) if (sensorList[i][0] <=xrange and sensorList[i][1] <=yrange): PH_list_sensors[i] = oil_spill_PH def reset_oil(sensorList, PH_list_sensors): normal_PH = 7.5 for i in range(len(sensorList)): PH_list_sensors[i] = normal_PH def SRP_toggler(state_s, s_counter, PH_list_sensors, Fin_Conn_S_R, Fin_Conn_R_R, round, srpfile): ### The following code is divided into 2 blocks. #Block 1 is for Normal operation of SRP and Block 2 is behaviour after oil detection ### Block 1: # print("check for oil leaks:") oil_affected_relays = Master_PH_checker(PH_list_sensors, Fin_Conn_S_R, state_s) # print(oil_affected_relays) if (oil_affected_relays==[]):##if no oil spill detected then run normal operation # print("No leaks detected.\n proceed to normal protocol") ##toggle 1/4 th sensors: #this loop iterates over a cluster of relay. j gives us the relay index for j in range(len(s_counter)): counter_temp = s_counter[j][0] ##number of sensors in the cluster starting_sensor = s_counter[j][1] ## stores which sensor to start with (does not stores the actual sensors index) counter_1_4_th = s_counter[j][0]//4 ##divides the cluster into four parts ##check if all 4 batches have been activated. If so then reset the starting sensor if(starting_sensor >= len(s_counter[j])-1 and counter_temp!=0): srpfile.write("For "+ str(j) +"'th relay. resetting cycle\n") # print("For", j, "'th relay. resetting cycle") s_counter[j][1] = 2 ##reset the starting sensor i.e. start from the 1st sensor (i.e. with index 2 ) starting_sensor = s_counter[j][1] ##iterating over the list to turn off all sensors and turn on the 1/4th batch counting = 0 starting_detected = False for k in range(counter_temp+2): ##skip the first two indexes as they contain the count and the starting point of the sensor batch if(k >1): #check if the index is the starting point if(s_counter[j][k]==s_counter[j][starting_sensor]): starting_detected = True #check if the starting sensor is detected and 1/4th of the sensors are not activated. turn on the sensor if(counter_1_4_th!=0): ##if counter_1_4_th is zero then the cluster remains on and no switching is required if(starting_detected and counting < counter_1_4_th): state_s[s_counter[j][k]][j] = 1 counting+=1 s_counter[j][1]+=1 ##update the starting sensor #else turn off the sensor else: state_s[s_counter[j][k]][j] = 0 else: ##the cluster will have all sensors turned on state_s[s_counter[j][k]][j] = 1 counting+=1 ##priniting sensors conncted to a relay if(counter_temp!=0): srpfile.write("Relay: "+ str(j) + " has "+ str(counting)+ " active sensors out of "+ str(len(s_counter[j]) -2)+ " sensors\n") # print("Relay: ", j , " has ", counting, " active sensors out of ", len(s_counter[j]) -2, " sensors") else: srpfile.write("Oil Detected in Round: "+ str(round)+" "+str(len(x.nodes)-1) +"\n") print("Oil Detected in Round: ", round+len(x.nodes)-1) #turn on all sensors in the oil detected relays: # print("Oil leak detected. Turning on all sensors in all affected clusters") srpfile.write("Oil leak detected. Turning on all sensors in all affected clusters\n") for i in oil_affected_relays: for j in range(len(state_s)): if(Fin_Conn_S_R[j][i]==1): #check if the sensor is connected to the relay ##turn it on: state_s[j][i] = 1 # print("check neighboring cluster heads PH") # #alert its neighbors and add their neighbors to the oil_affected_relays list: # oil_affected_temp = oil_affected_relays[:] # while(oil_affected_temp!=[]): # print("in loop") # check_cluster = oil_affected_temp.pop(0) ##cluster to be checked for spill # if(PH_checker(PH_list_sensors, Fin_Conn_S_R, state_s, check_cluster)): ##if true then check neighbors aswell # new_clusters = add_neighbour(check_cluster, Fin_Conn_R_R, oil_affected_relays) # oil_affected_relays.append(new_clusters) ##add to the master relay list # oil_affected_relays.append(new_clusters) ##add to the temp relay list for stack implimentation def simu_network(nw_e_s, nw_e_r, state_s): #intializing the rounds file = open("percentage_network.txt", "w") srpfile = open("srp_output.txt", "w") file.write("sensors: " +str(sen) +"\n") file.write("Relays: " + str(len(connection)) +"\n") file.write(str(connection)) file.write("\n") total_energy = 0 round = 0 dead_s = 0 dead_r = 0 #running the simulation for n rounds while(True): consumed_round_energy = 0 #initializing dead sensors # print("ROUND: ", round) file.write("ROUND: " + str(round)+ "\n") srpfile.write("ROUND: " + str(round)+ "\n") # if(dead_r>0): # # print("Ah ha!") # break #updating all sensors ##this loop iterates over all the relays # print("sending/receiving data") for i in range(len(Fin_Conn_S_R[0])): ##this loop iterates over all sensors # print("sending data from sensor") for j in range(len(Fin_Conn_S_R)): #checking if the sensor is deployed as well as not asleep as well as not dead if(Fin_Conn_S_R[j][i]== 1 and state_s[j][i] == 1): #check if a node died if(nw_e_s[j][i]- e_s[j][i] <=0): # print("sensor ", j, " died") # strings = "sensor " + str(j) + " died" +"\n" # file.write(strings) Fin_Conn_S_R[j][i]=0 ##sensor dies dead_s+=1 else: ##transmit data to the relay # print("transmission energy: ", e_s[j][i], " residual energy of sensor: ", nw_e_s[j][i]-e_s[j][i]) # strings = "transmission energy: " + str(e_s[j][i]) +" residual energy of sensor: " +str(nw_e_s[j][i]-e_s[j][i]) +"\n" # file.write(strings) nw_e_s[j][i] -= e_s[j][i] consumed_round_energy+=e_s[j][i] ##relay connected to steiner relays: for w in range(len(steiner_R_R)): if (steiner_R_R[i][w] ==1): if(nw_e_r[i][w]-e_r[i][w][0] >0): nw_e_r[i][w]-=e_r[i][w][0] consumed_round_energy+=e_r[i][w][0] else: dead_r+=1 steiner_R_R[i][w]=0 # print("sending/receiving data on relays") ##this loop iterates over all relays connected to i'th relay for k in range(len(Fin_Conn_R_R)): ##check if the two relays are connected. If yes then exchange data # print("checking relay connection", Fin_Conn_R_R[k][i]) if(Fin_Conn_R_R[k][i] == 1): #check if node is dead: # print("connection relay found") if (nw_e_r[k][i] - e_r[k][i][0] <= 0): Fin_Conn_R_R[k][i]=0 for m in range(len(Fin_Conn_S_R)): ##turn off all sensors connected to the relays if(Fin_Conn_S_R[m][i]!=0): Fin_Conn_S_R[m][i] = 0 dead_s+=1 # print("A relay with energy: ", nw_e_r[k][i], "died") # strings = "A relay with energy: "+str(nw_e_r[k][i]) +"died" + "\n" # file.write(strings) dead_r+=1 else: # print("transmission energy: ", e_r[k][i], " residual energy of relay: ", nw_e_s[k][i]-e_s[k][i]) # strings = "transmission energy: " +str(e_r[k][i])+ " residual energy of relay: "+ str(nw_e_s[k][i]-e_s[k][i]) +"\n" # file.write(strings) nw_e_r[k][i] -= e_r[k][i][0] consumed_round_energy+=e_r[k][i][0] #check for connected sensors: #this loop checks for connected sensors to a relay for l in range(len(Fin_Conn_S_R)): #gives index of sensor if(Fin_Conn_S_R[l][i]==1 and state_s[l][i] ==1): if((nw_e_r[k][i] - e_r[k][i][1]) > 0): # print("receival energy: ", e_r[k][i], " residual energy of relay: ", nw_e_r[k][i]-e_r[k][i][1]) # strings = "receival energy: "+ str(e_r[k][i])+ " residual energy of relay: "+ str(nw_e_r[k][i]-e_r[k][i][1]) +"\n" # file.write(strings) nw_e_r[k][i]-= e_r[k][i][1] ##receive data from sensor consumed_round_energy+=e_r[k][i][1] else: Fin_Conn_R_R[k][i]=0 dead_r+=1 for m in range(len(Fin_Conn_S_R)):##turn off all sensors connected to the relays if(Fin_Conn_S_R[m][i]!=0): Fin_Conn_S_R[m][i] = 0 dead_s+=1 ##This loop aggregates data for m in range(len(Fin_Conn_R_R)): if(Fin_Conn_R_R[m][i]==1): nw_e_r[m][i]-=e_r[m][i][2] consumed_round_energy+=e_r[m][i][2] dead_s_pc = (dead_s/sen)*100 # print("dead relays: ", dead_r) strings = "dead relays: " + str(dead_r) +"\n" file.write(strings) dead_r_pc = (dead_r/len(x.nodes))*100 dead_nw_pc =( dead_s_pc + dead_r_pc)/2 # print("Dead Network pc: ", dead_nw_pc, " %") strings = "Dead Network pc: " + str(dead_nw_pc) + " % \n" file.write(strings) # print("Dead Sensor pc: ", dead_s_pc, " %") strings = "Dead Sensor pc: "+ str(dead_s_pc) + " % \n" file.write(strings) # print("Dead relays pc: ", dead_r_pc, " %") strings = "Dead relays pc: "+ str(dead_r_pc)+ " % \n" file.write(strings) ##at round 100 spill oil: if(round==30): oil_simulator(Fin_Conn_S_R, sensorList, PH_list_sensors) print("Spilling oil") if(round== 30+len(x.nodes) - 1): reset_oil(sensorList, PH_list_sensors) if( dead_s_pc > 90 or dead_r_pc >90): break # print("Energy matrix Relay:") # for x in e_r: # print(x) # print("Energy matrix Sensor:") # for x in e_s: # print(x) ##toggles the state of the sensors (SRP implimented here) SRP_toggler(state_s, s_counter, PH_list_sensors, Fin_Conn_S_R, Fin_Conn_R_R, round, srpfile) #output energy used in the round: total_energy+=consumed_round_energy # print("Energy used in round:", consumed_round_energy) srpfile.write("Energy used in round: " + str(consumed_round_energy) + "\n") ##update round round+=1 file.write("total rounds: "+ str(round)+"\n") # print("total rounds:", round) file.write("total Energy used: "+ str(total_energy) +"\n") # print("total Energy used:", total_energy) file.close() srpfile.close() return nw_e_s network_energy_s = simu_network(nw_e_s, nw_e_r, state_s) file = open("energy matrix.txt", "a") file.write("sensor residual matrix"+"\n") for i in network_energy_s: file.write(str(i)) file.write("\n") # print(i) file.write("Relay residual matrix"+"\n") for i in nw_e_r: file.write(str(i)+"\n") file.write("Total energy used: "+ str(gamma) +"\n") file.close() print(s_counter) return len(x.nodes), gamma
def Optimize(rad, sen): width = 100.0 ##100 by 100 meters of square field R = float(rad) ##Radius of communication of each node relayConstraint = 121 ##Total relays to be deployed sensorList = [] relayList = [] ##Populating sensorList and relayList for i in range(sen): sensorList.append((round(random.uniform(0.0, width), 6), round(random.uniform(0.0, width), 6))) row = 0 col = 0 while row <= width: while col <= width: relayList.append((float(row), float(col))) col += 10 row += 10 col = 0 ##Calculates the Euclidean distance def distance(a, b): return math.sqrt((a**2) + (b**2)) ##Calculates the Connectivity matrix for sensor x relay layer def Connection_s_r(SN, RN): Neighbor_Sensor_Relay = [[[0] for i in range(len(RN))] for j in range(len(SN))] for i in range(len(SN)): for j in range(len(RN)): dist = distance(abs(SN[i][0] - RN[j][0]), abs(SN[i][1] - RN[j][1])) if dist <= R: Neighbor_Sensor_Relay[i][j] = 1 elif dist > R: Neighbor_Sensor_Relay[i][j] = 0 return Neighbor_Sensor_Relay n_s_r = (Connection_s_r(sensorList, relayList)) ###print('Neighbor Matrix of Sensor x Relay Layer', '\n') ###for i in n_s_r: ### print(i) ##Calculates the Connectivity matrix for relay x relay layer def Connection_r_r(RN): Neighbor_Relay_Relay = [[[0] for i in range(len(RN))] for j in range(len(RN))] for i in range(len(RN)): for j in range(len(RN)): dist = distance(abs(RN[i][0] - RN[j][0]), abs(RN[i][1] - RN[j][1])) if i == j: Neighbor_Relay_Relay[i][j] = 0 elif dist <= R: Neighbor_Relay_Relay[i][j] = 1 elif dist > R: Neighbor_Relay_Relay[i][j] = 0 return Neighbor_Relay_Relay n_r_r = (Connection_r_r(relayList)) ###print('Neighbor Matrix of Relay x Relay Layer', '\n') ###for i in n_r_r: ### print(i) ##Calculates the Data link flow matrix for sensor x relay layer def Link_s_r(SN, RN): bandwidth = 100.0 Linkflow_Sensor_Relay = [[[0] for i in range(len(RN))] for j in range(len(SN))] for i in range(len(SN)): for j in range(len(RN)): Linkflow_Sensor_Relay[i][j] = bandwidth return Linkflow_Sensor_Relay l_s_r = (Link_s_r(sensorList, relayList)) ###print('Data Link Flow Matrix of Sensor x Relay Layer', '\n') ###for i in l_s_r: ### print(i) ##Calculates the Data link flow matrix for relay x relay layer def Link_r_r(RN): bandwidth = 200.0 Linkflow_Relay_Relay = [[[0] for i in range(len(RN))] for j in range(len(RN))] for i in range(len(RN)): for j in range(len(RN)): if i != j: Linkflow_Relay_Relay[i][j] = bandwidth else: Linkflow_Relay_Relay[i][j] = 0 return Linkflow_Relay_Relay l_r_r = (Link_r_r(relayList)) ###print('Data Link Flow Matrix of Relay x Relay Layer', '\n') ###for i in l_r_r: ### print(i) ##Calculates the Energy matrix for sensor x relay layer def Energy_s_r(SN, RN): bandwidth = 100.0 Energy_Sensor_Relay = [[[0] for i in range(len(RN))] for j in range(len(SN))] E_radio_S = 50.0 * (10**(-9)) E_radio_R = 100.0 * (10**(-9)) Transmit_amplifier = 100.0 * (10**(-12)) for i in range(len(SN)): for j in range(len(RN)): dist = distance(abs(SN[i][0] - RN[j][0]), abs(SN[i][1] - RN[j][1])) energy_sensor_tx = float( bandwidth * (E_radio_S + (Transmit_amplifier * (dist**2)))) ##energy used when sensor transmits data energy_relay_rx = float( bandwidth * E_radio_R) ##energy used when relay receives data total_energy = energy_sensor_tx + energy_relay_rx Energy_Sensor_Relay[i][ j] = total_energy / 4 ##Only 1/4 sensors will be active per time unit for every relay return Energy_Sensor_Relay e_s_r = (Energy_s_r(sensorList, relayList)) ###print('Energy Matrix of Sensor x Relay layer', '\n') ###for i in e_s_r: ### print(i) ##Calculates the Energy matrix for relay x relay layer def Energy_r_r(RN): bandwidth = 200.0 Energy_Relay_Relay = [[[0] for i in range(len(RN))] for j in range(len(RN))] E_radio_R = 100.0 * (10**(-9)) Transmit_amplifier = 100.0 * (10**(-12)) for i in range(len(RN)): for j in range(len(RN)): dist = distance(abs(RN[i][0] - RN[j][0]), abs(RN[i][1] - RN[j][1])) energy_relay_tx = float( bandwidth * (E_radio_R + (Transmit_amplifier * (dist**2)))) ##energy used when relay transmits data energy_relay_rx = float( bandwidth * E_radio_R) ##energy used when relay receives data total_energy = (energy_relay_tx + energy_relay_rx) Energy_Relay_Relay[i][j] = total_energy return Energy_Relay_Relay e_r_r = (Energy_r_r(relayList)) ###print('Energy Matrix of Relay x Relay layer', '\n') ###for i in e_r_r: ### print(i) Problem = LpProblem("The Energy Problem", LpMinimize) Var_S_R = [ pulp.LpVariable(str('SR' + str(i) + "_" + str(j)), 0, 1, pulp.LpInteger) for i in range(len(sensorList)) for j in range(len(relayList)) ] Var_R_R = [ pulp.LpVariable(str('RR' + str(i) + "_" + str(j)), 0, 1, pulp.LpInteger) for i in range(len(relayList)) for j in range(len(relayList)) ] Bool_Var = [ pulp.LpVariable(str('B' + str(i)), 0, 1, pulp.LpInteger) for i in range(len(relayList)) ] k = 0 ##for variable indexing objective = [] ##for objective function linkflow_column = [] ##for data rate constraint conn_SR = [] ##for connection constraint conn_RR = [] while k < len(Var_S_R): for i in range(len(e_s_r)): for j in range(len(e_s_r[i])): objective.append(e_s_r[i][j] * Var_S_R[k]) conn_SR.append(n_s_r[i][j] * Var_S_R[k]) k += 1 Problem += lpSum(conn_SR) == 1 conn_SR = [] B_Max = 3000 ##by controlling the maximum data rate we control number of sensors per relay B_SR = 100 S_Per_R = B_Max / B_SR ##contains the number of sensor per relay constraint applied above (a relay can have a 100 data rate with a sensor) Var_alias_SR = [] for i in Var_S_R: Var_alias_SR.append(str(i)) booleansumcolumn = [] ##holds the variables for each column at a time for i in range(len(relayList)): for j in range(len(sensorList)): e = Var_alias_SR.index( str('SR' + str(j) + "_" + str(i))) ##iterating column wise on the SxR matrix booleansumcolumn.append(Var_S_R[e]) linkflow_column.append(l_s_r[j][i] * Var_S_R[e]) Problem += lpSum(booleansumcolumn) >= Bool_Var[ i] ##1. OR gate for constraining number of relays Problem += lpSum(linkflow_column) <= ( B_Max ) ##controlling number of sensors per relay by constraining the total data rate received by an individual relay for c in booleansumcolumn: Problem += c <= Bool_Var[ i] ##2. OR gate for constraining number of relays booleansumcolumn = [ ] ##emptying the columns to fill the variables of the next column linkflow_column = [] k = 0 while k < len(Var_R_R): for i in range(len(e_r_r)): for j in range(len(e_r_r[i])): objective.append(e_r_r[i][j] * Var_R_R[k]) conn_RR.append(n_r_r[i][j] * Var_R_R[k]) k += 1 Problem += lpSum(conn_RR) >= 0 conn_RR = [] Var_alias_RR = [] for i in Var_R_R: Var_alias_RR.append(str(i)) booleansumcolumn = [] ##holds the variables for each column at a time for i in range(len(relayList)): for j in range(len(relayList)): e = Var_alias_RR.index( str('RR' + str(j) + "_" + str(i))) ##iterating column wise on the RxR matrix booleansumcolumn.append(Var_R_R[e]) Problem += lpSum(booleansumcolumn) >= Bool_Var[ i] ##1. OR gate for constraining number of relays for c in booleansumcolumn: Problem += c <= Bool_Var[ i] ##2. OR gate for constraining number of relays booleansumcolumn = [ ] ##emptying the columns to fill the variables of the next column for i in range(len(relayList) ): ##making sure that the relay x relay matrix is symmetric for j in range(len(relayList)): if i != j: e = Var_alias_RR.index(str('RR' + str(j) + "_" + str(i))) f = Var_alias_RR.index(str('RR' + str(i) + "_" + str(j))) Problem += Var_R_R[e] == Var_R_R[f] elif i == j: ##controversial condition f = Var_alias_RR.index(str('RR' + str(i) + "_" + str(j))) Problem += Var_R_R[f] == 0 Problem += lpSum( objective ) ##adding the objective energy linear combination to minimize Problem += lpSum( Bool_Var) <= relayConstraint ##number of relays constraint finally ###print(Problem) t1 = time.clock() Problem.solve( solvers.PULP_CBC_CMD(fracGap=0.0000000000001)) ##solving the problem t2 = time.clock() ##this part (below) is just to print and understand how the code works DeployedRelays_SR = [] DeployedRelays_RR = [] for v in Problem.variables(): ###print(v.name, "=", v.varValue) if v.varValue == 1.0 and v.name in Var_alias_SR: DeployedRelays_SR.append(v.name) elif v.varValue == 1.0 and v.name in Var_alias_RR: DeployedRelays_RR.append(v.name) Fin_Conn_S_R = [[0 for i in range(len(relayList))] for j in range(len(sensorList)) ] ##the output matrix produced by the LP solver Fin_Conn_R_R = [[0 for i in range(len(relayList))] for j in range(len(relayList))] b = 0 ##used to keep track of the index number of the string for i in DeployedRelays_SR: for j in i: if j != "_": b += 1 else: break s = int(i[2:b]) ##stores the indexes of SxR matrix that are 1(high) r = int(i[b + 1:]) Fin_Conn_S_R[s][r] = 1 b = 0 ##print('Final Optimized Connectivity Matrix of Sensor x Relay layer: ', '\n') ##for i in Fin_Conn_S_R: ## print(i) b = 0 ##used to keep track of the index number of the string for i in DeployedRelays_RR: for j in i: if j != "_": b += 1 else: break s = int(i[2:b]) ##stores the indexes of RxR matrix that are 1(high) r = int(i[b + 1:]) Fin_Conn_R_R[s][r] = 1 b = 0 ##print('Final Optimized Connectivity Matrix of Relay x Relay layer: ', '\n') ##for i in Fin_Conn_R_R: ## print(i) v = [] connection = {} for i in range(len(Fin_Conn_S_R)): for j in range(len(Fin_Conn_S_R[i])): if Fin_Conn_S_R[i][j] == 1 and j not in v: connection['Relay' + str(j)] = ['Sensor' + str(i)] v.append(j) elif Fin_Conn_S_R[i][j] == 0: pass else: connection['Relay' + str(j)].append('Sensor' + str(i)) ##this part (above) is just to print and understand how the code works # print("Optimum Relays Used: ") # print(sorted(connection),"\n") # print("The Relay-Sensor dictionary: \n") # print(connection, "\n") # print ("Status:", LpStatus[Problem.status]) # print("Number of Candidate Location for Relays: ", len(relayList)) # print("Number of Sensors in the Network: ", len(sensorList)) # print('Number Of Relays Deployed on the Candidate Locations: ', len(connection)) # print("Communication Radius of the Each Node: ", R) # print("Number of Base stations: ", 1) # print("Time Taken to Calculate energy: ", t2-t1) gamma = value( Problem.objective) ##total optimum energy without steiner problem # print("Energy of the network before steiner node deployment: ", value(Problem.objective)) ##now checking if the assumption was correct or do we need to add more relay nodes for a single path? (steiner tree problem) def relayNumtoInt(string): i = 0 while not string[i].isdigit(): i += 1 return int(string[i:]) - 1 connection_list = sorted(connection) terminal_nodes = ([ relayNumtoInt(connection_list[i]) for i in range(len(connection_list)) ]) diction = { i: [j for j, adjacent in enumerate(row) if adjacent] for i, row in enumerate(n_r_r) } G = nx.Graph(diction) x = steiner_tree(G, terminal_nodes) ##returns all the nodes that form a tree if len(x.nodes) == len(terminal_nodes): print('No stiener nodes required.') else: ##if additional nodes are deployed we add their energies too B_R = 200.0 ##taking approximately largest distance dist = R E_radio_R = 100.0 * (10**(-9)) Transmit_amplifier = 100.0 * (10**(-12)) energy_relay = (B_R * E_radio_R) + B_R * (E_radio_R + Transmit_amplifier * (dist**2)) for i in range(len((set(x.nodes) - set(terminal_nodes)))): gamma += (energy_relay) ##adding relay energy of steiner nodes ##print('Additional steiner nodes deployed ', (set(x.nodes) - set(terminal_nodes))) a = 0 for i in connection: ##calculating maximum sensor per relay a = max(a, len(connection[i])) ## print("Maximum Sensors per Relay in the Network: ", a) ## print("Maximum Sensor per Relay constraint of the Network: " , S_Per_R) ## print("Maximum Relay constraint of the Network: ", relayConstraint) ## print("Total Energy of the network after steiner nodes: ", gamma) ## print("Total Number of relays deployed after steiner nodes: ", len(x.nodes)) return len(x.nodes), gamma
def create_graph_from_dominating_set(G, domin_set): return steiner_tree(G, domin_set)
def calc_steiner_spanning_tree( crs_projected, input_network_shp, output_network_folder, building_nodes_shp, output_edges, output_nodes, weight_field, type_mat_default, pipe_diameter_default, type_network, total_demand_location, create_plant, allow_looped_networks, optimization_flag, plant_building_names, disconnected_building_names): """ Calculate the minimum spanning tree of the network. Note that this function can't be run in parallel in it's present form. :param str crs_projected: e.g. "+proj=utm +zone=48N +ellps=WGS84 +datum=WGS84 +units=m +no_defs" :param str input_network_shp: e.g. "TEMP/potential_network.shp" :param str output_network_folder: "{general:scenario}/inputs/networks/DC" :param str building_nodes_shp: e.g. "%TEMP%/nodes_buildings.shp" :param str output_edges: "{general:scenario}/inputs/networks/DC/edges.shp" :param str output_nodes: "{general:scenario}/inputs/networks/DC/nodes.shp" :param str weight_field: e.g. "Shape_Leng" :param str type_mat_default: e.g. "T1" :param float pipe_diameter_default: e.g. 150 :param str type_network: "DC" or "DH" :param str total_demand_location: "{general:scenario}/outputs/data/demand/Total_demand.csv" :param bool create_plant: e.g. True :param bool allow_looped_networks: :param bool optimization_flag: :param List[str] plant_building_names: e.g. ``['B001']`` :param List[str] disconnected_building_names: e.g. ``['B002', 'B010', 'B004', 'B005', 'B009']`` :return: ``(mst_edges, new_mst_nodes)`` """ # read shapefile into networkx format into a directed graph, this is the potential network graph = nx.read_shp(input_network_shp) nodes_graph = nx.read_shp(building_nodes_shp) # tolerance tolerance = 6 # transform to an undirected graph iterator_edges = graph.edges(data=True) # get graph G = nx.Graph() for (x, y, data) in iterator_edges: x = (round(x[0], tolerance), round(x[1], tolerance)) y = (round(y[0], tolerance), round(y[1], tolerance)) G.add_edge(x, y, weight=data[weight_field]) # get nodes iterator_nodes = nodes_graph.nodes(data=False) terminal_nodes = [(round(node[0], tolerance), round(node[1], tolerance)) for node in iterator_nodes] if len(disconnected_building_names) > 0: # identify coordinates of disconnected buildings and remove form terminal nodes list all_building_nodes_df = gdf.from_file(building_nodes_shp) all_building_nodes_df.loc[:, 'coordinates'] = all_building_nodes_df[ 'geometry'].apply(lambda x: (round(x.coords[0][0], tolerance), round(x.coords[0][1], tolerance))) disconnected_building_coordinates = [] for building in disconnected_building_names: # skip disconnected buildings that were filtered out because they had no demand if building in list(all_building_nodes_df['Name']): index = np.where(all_building_nodes_df['Name'] == building)[0] disconnected_building_coordinates.append( all_building_nodes_df['coordinates'].values[index][0]) for disconnected_building in disconnected_building_coordinates: terminal_nodes = [ i for i in terminal_nodes if i != disconnected_building ] # calculate steiner spanning tree of undirected graph try: mst_non_directed = nx.Graph(steiner_tree(G, terminal_nodes)) except: raise ValueError( 'There was an error while creating the Steiner tree. ' 'Check the streets.shp for isolated/disconnected streets (lines) and erase them, ' 'the Steiner tree does not support disconnected graphs.') nx.write_shp(mst_non_directed, output_network_folder) # writes nodes.shp and edges.shp # populate fields Building, Type, Name mst_nodes = gdf.from_file(output_nodes) building_nodes_df = gdf.from_file(building_nodes_shp) building_nodes_df.loc[:, 'coordinates'] = building_nodes_df['geometry'].apply( lambda x: (round(x.coords[0][0], tolerance), round(x.coords[0][1], tolerance))) # if there are disconnected buildings if len(disconnected_building_names) > 0: for disconnected_building in disconnected_building_coordinates: building_id = int( np.where(building_nodes_df['coordinates'] == disconnected_building)[0]) building_nodes_df = building_nodes_df.drop( building_nodes_df.index[building_id]) mst_nodes.loc[:, 'coordinates'] = mst_nodes['geometry'].apply(lambda x: ( round(x.coords[0][0], tolerance), round(x.coords[0][1], tolerance))) names_temporary = ["NODE" + str(x) for x in mst_nodes['FID']] new_mst_nodes = mst_nodes.merge(building_nodes_df, suffixes=['', '_y'], on="coordinates", how='outer') new_mst_nodes.fillna(value="NONE", inplace=True) new_mst_nodes.loc[:, 'Building'] = new_mst_nodes['Name'] new_mst_nodes.loc[:, 'Name'] = names_temporary new_mst_nodes.loc[:, 'Type'] = new_mst_nodes['Building'].apply( lambda x: 'CONSUMER' if x != "NONE" else x) # populate fields Type_mat, Name, Pipe_Dn mst_edges = gdf.from_file(output_edges) mst_edges.loc[:, 'Type_mat'] = type_mat_default mst_edges.loc[:, 'Pipe_DN'] = pipe_diameter_default mst_edges.loc[:, 'Name'] = ["PIPE" + str(x) for x in mst_edges.index] if allow_looped_networks == True: # add loops to the network by connecting None nodes that exist in the potential network mst_edges, new_mst_nodes = add_loops_to_network( G, mst_non_directed, new_mst_nodes, mst_edges, type_mat_default, pipe_diameter_default) # mst_edges.drop(['weight'], inplace=True, axis=1) if create_plant: if optimization_flag == False: building_anchor = calc_coord_anchor(total_demand_location, new_mst_nodes, type_network) new_mst_nodes, mst_edges = add_plant_close_to_anchor( building_anchor, new_mst_nodes, mst_edges, type_mat_default, pipe_diameter_default) else: for building in plant_building_names: building_anchor = building_node_from_name( building, new_mst_nodes) new_mst_nodes, mst_edges = add_plant_close_to_anchor( building_anchor, new_mst_nodes, mst_edges, type_mat_default, pipe_diameter_default) fields_nodes = ['Name', 'Building', 'Type', 'geometry'] new_mst_nodes = new_mst_nodes[fields_nodes] mst_edges['length_m'] = mst_edges['weight'] fields_edges = ['Name', 'length_m', 'Pipe_DN', 'Type_mat', 'geometry'] mst_edges = mst_edges[fields_edges] nx.write_shp(mst_non_directed, output_network_folder) # get coordinate system and reproject to UTM mst_edges.crs = crs_projected new_mst_nodes.crs = crs_projected mst_edges.to_file(output_edges, driver='ESRI Shapefile') new_mst_nodes.to_file(output_nodes, driver='ESRI Shapefile')
def solve(self, list_of_locations, list_of_homes, starting_car_location, adjacency_matrix, params=[]): """ Write your algorithm here. Input: list_of_locations: A list of locations such that node i of the graph corresponds to name at index i of the list list_of_homes: A list of homes starting_car_location: The name of the starting location for the car adjacency_matrix: The adjacency matrix from the input file Output: A list of locations representing the car path A list of (location, [homes]) representing drop-offs """ adj = adjacency_matrix s = starting_car_location L = list_of_locations homes_numbered = [L.index(i) for i in list_of_homes] self.homes = homes_numbered G = student_utils.adjacency_matrix_to_graph(adj)[0] # show_graph(G) s = L.index(s) H = [L.index(i) for i in list_of_homes] + [s] G_steinertree = (steinertree.steiner_tree(G, H)) directed_steinertree = nx.dfs_tree(G_steinertree, s) self.prune_tree(directed_steinertree, s) drop_off_locs = set(self.drop_off.values()) pruned_homes = set(self.drop_off.keys()) for home in homes_numbered: if not (home in pruned_homes): drop_off_locs.add(home) p, d = nx.floyd_warshall_predecessor_and_distance(G) path = (self.nearest_neighbors(drop_off_locs, d, s, p)) # pos = hierarchy_pos(directed_steinertree, s) # nx.draw(directed_steinertree, pos=pos, with_labels=True) # plt.show() final_dict = {i: [] for i in drop_off_locs} for i in homes_numbered: if i in drop_off_locs: final_dict[i].append(i) else: final_dict[self.drop_off[i]].append(i) with_steiner = path, final_dict without_steiner_path = (self.nearest_neighbors(H, d, s, p)) without_steiner_dict = {i: [i] for i in homes_numbered} self.drop_off = {} self.prune_tree(directed_steinertree, s) # pos = hierarchy_pos(directed_steinertree, s) # nx.draw(directed_steinertree, pos=pos, with_labels=True) # plt.show() drop_off_locs = set(self.drop_off.values()) pruned_homes = set(self.drop_off.keys()) for home in homes_numbered: if not (home in pruned_homes): drop_off_locs.add(home) p, d = nx.floyd_warshall_predecessor_and_distance(G) path_double_pruned = (self.nearest_neighbors(drop_off_locs, d, s, p)) final_dict_double_pruned = {i: [] for i in drop_off_locs} for i in homes_numbered: if i in drop_off_locs: final_dict_double_pruned[i].append(i) else: final_dict_double_pruned[self.drop_off[i]].append(i) return min([ with_steiner, (without_steiner_path, without_steiner_dict), (path_double_pruned, final_dict_double_pruned) ], key=lambda x: cost_of_solution(G, x[0], x[1]))
edges = [] filename = os.path.basename(target_path) with open(target_path, 'r') as tar: lines = tar.read().splitlines() for line in lines: if line[:2] == "E ": parsed_line = line.split() a = int(parsed_line[1]) b = int(parsed_line[2]) w = float(parsed_line[3]) G.add_edge(a, b, weight=w) edges.append((a, b, w)) elif line[:2] == "T ": parsed_line = line.split() t = int(parsed_line[1]) terminals.append(t) steiner_tree_ = steiner_tree(G, terminals, weight="weight") minimum_spanning_tree_ = minimum_spanning_tree(G, weight='weight') # nx.draw(steiner_tree_, with_labels=True) print( os.path.basename(target_path) + ": " + 'n=' + str(len(G.nodes())) + ' ' + 'e=' + str(len(G.edges())) + ' ' + 't=' + str(len(terminals)) + ' ' + str(steiner_tree_.size(weight="weight"))) # save graph nx.write_weighted_edgelist(G, os.path.join(save_path, filename)) # save terminals with open(os.path.join(save_path, filename + ".terminals"), 'w') as f: for t in terminals: f.write(str(t) + '\n')
def determine_with_steiner_tree(names, C, queryobject, idx_charter_location=[], maxlen=5): maybeV, maybesolution, maybecost = _pre_check_and_get_candidates( names, C, queryobject, idx_charter_location=idx_charter_location) if not maybeV: return maybesolution, maybecost V = maybeV logging.debug("instating candidate graph for {} names".format(len(names))) G = nx.Graph() if len(names) > maxlen * 2 - 1: logging.debug("number of names ({}) exceeds set limit ({})... \ partitioning the name set and solving individual parts \ to reduce candidates".format(len(names), maxlen)) logging.debug("heuristic starts...") V = topksteiner(names, C, queryobject, idx_charter_location, k=2, random_starts=5, n=maxlen) logging.debug("finished...current workload, \ placenamecandidates={},".format([len(x) for x in V])) for i, idxset in enumerate(V): for j, idxset_other in enumerate(V): if i >= j: continue for idx in idxset: for idx_other in idxset_other: if not G.has_edge(idx, idx_other): G.add_edge(idx, idx_other, weight=queryobject.cost( names[i], idx, names[j], idx_other, idx_charter_location)) logging.debug("current workload, placenamecandidates={},".format( [len(x) for x in V])) very_large_number = 10000.00 for i, name in enumerate(names): for j, idx in enumerate(V[i]): G.add_edge("name:" + name, idx, weight=very_large_number) res = steinertree.steiner_tree(G, ["name:" + n for i, n in enumerate(names)]) #res = steinertree.steiner_tree(res,["name:"+n for n in names]) res = nx.Graph(res) triples = [a for a in res.edges(data=True)] logging.debug("Steiner graph triples: {}".format(triples)) #gather solutions sols = [] for i, n in enumerate(names): sols.append(_retrieve("name:" + n, res)) res.remove_node("name:" + n) cum_weight = _get_cum_weight(sols, graph=None, query_object=queryobject, names=names, idx_charter_location=idx_charter_location) logging.debug("minimum cost: {}, result of hillclimbing: {}".format( cum_weight, sols)) return sols, cum_weight
def calc_steiner_spanning_tree(input_network_shp, output_network_folder, building_nodes_shp, output_edges, output_nodes, weight_field, type_mat_default, pipe_diameter_default, type_network, total_demand_location, create_plant, allow_looped_networks): # read shapefile into networkx format into a directed graph, this is the potential network graph = nx.read_shp(input_network_shp) nodes_graph = nx.read_shp(building_nodes_shp) # transform to an undirected graph iterator_edges = graph.edges(data=True) #get graph G = nx.Graph() for (x, y, data) in iterator_edges: x = (round(x[0], 4), round(x[1], 4)) y = (round(y[0], 4), round(y[1], 4)) G.add_edge(x, y, weight=data[weight_field]) #get nodes iterator_nodes = nodes_graph.nodes(data=False) terminal_nodes = [(round(node[0], 4), round(node[1], 4)) for node in iterator_nodes] # calculate steiner spanning tree of undirected graph mst_non_directed = nx.Graph(steiner_tree(G, terminal_nodes)) nx.write_shp(mst_non_directed, output_network_folder) # populate fields Building, Type, Name mst_nodes = gdf.from_file(output_nodes) buiding_nodes_df = gdf.from_file(building_nodes_shp) buiding_nodes_df['coordinates'] = buiding_nodes_df['geometry'].apply( lambda x: (round(x.coords[0][0], 4), round(x.coords[0][1], 4))) mst_nodes['coordinates'] = mst_nodes['geometry'].apply( lambda x: (round(x.coords[0][0], 4), round(x.coords[0][1], 4))) names_temporary = ["NODE" + str(x) for x in mst_nodes['FID']] new_mst_nodes = mst_nodes.merge(buiding_nodes_df, suffixes=['', '_y'], on="coordinates", how='outer') new_mst_nodes.fillna(value="NONE", inplace=True) new_mst_nodes['Building'] = new_mst_nodes['Name'] new_mst_nodes['Name'] = names_temporary new_mst_nodes['Type'] = new_mst_nodes['Building'].apply( lambda x: 'CONSUMER' if x != "NONE" else x) # populate fields Type_mat, Name, Pipe_Dn mst_edges = gdf.from_file(output_edges) mst_edges['Type_mat'] = type_mat_default mst_edges['Pipe_DN'] = pipe_diameter_default mst_edges['Name'] = ["PIPE" + str(x) for x in mst_edges.index] if allow_looped_networks == True: # add loops to the network by connecting None nodes that exist in the potential network mst_edges = add_loops_to_network(G, mst_non_directed, new_mst_nodes, mst_edges, type_mat_default, pipe_diameter_default) mst_edges.drop(['weight'], inplace=True, axis=1) if create_plant: building_anchor = calc_coord_anchor(total_demand_location, new_mst_nodes, type_network) new_mst_nodes, mst_edges = add_plant_close_to_anchor( building_anchor, new_mst_nodes, mst_edges, type_mat_default, pipe_diameter_default) new_mst_nodes.drop([ "FID", "coordinates", 'floors_bg', 'floors_ag', 'height_bg', 'height_ag', 'geometry_y' ], axis=1, inplace=True) nx.write_shp(mst_non_directed, output_network_folder) #get coordinate system and reproject to UTM mst_edges.to_file(output_edges, driver='ESRI Shapefile') new_mst_nodes.to_file(output_nodes, driver='ESRI Shapefile')
def calc_steiner_spanning_tree( crs_projected, temp_path_potential_network_shp, output_network_folder, temp_path_building_centroids_shp, path_output_edges_shp, path_output_nodes_shp, weight_field, type_mat_default, pipe_diameter_default, type_network, total_demand_location, create_plant, allow_looped_networks, optimization_flag, plant_building_names, disconnected_building_names): """ Calculate the minimum spanning tree of the network. Note that this function can't be run in parallel in it's present form. :param str crs_projected: e.g. "+proj=utm +zone=48N +ellps=WGS84 +datum=WGS84 +units=m +no_defs" :param str temp_path_potential_network_shp: e.g. "TEMP/potential_network.shp" :param str output_network_folder: "{general:scenario}/inputs/networks/DC" :param str temp_path_building_centroids_shp: e.g. "%TEMP%/nodes_buildings.shp" :param str path_output_edges_shp: "{general:scenario}/inputs/networks/DC/edges.shp" :param str path_output_nodes_shp: "{general:scenario}/inputs/networks/DC/nodes.shp" :param str weight_field: e.g. "Shape_Leng" :param str type_mat_default: e.g. "T1" :param float pipe_diameter_default: e.g. 150 :param str type_network: "DC" or "DH" :param str total_demand_location: "{general:scenario}/outputs/data/demand/Total_demand.csv" :param bool create_plant: e.g. True :param bool allow_looped_networks: :param bool optimization_flag: :param List[str] plant_building_names: e.g. ``['B001']`` :param List[str] disconnected_building_names: e.g. ``['B002', 'B010', 'B004', 'B005', 'B009']`` :return: ``(mst_edges, mst_nodes)`` """ # read shapefile into networkx format into a directed potential_network_graph, this is the potential network potential_network_graph = nx.read_shp(temp_path_potential_network_shp) building_nodes_graph = nx.read_shp(temp_path_building_centroids_shp) # transform to an undirected potential_network_graph iterator_edges = potential_network_graph.edges(data=True) G = nx.Graph() for (x, y, data) in iterator_edges: x = (round(x[0], SHAPEFILE_TOLERANCE), round(x[1], SHAPEFILE_TOLERANCE)) y = (round(y[0], SHAPEFILE_TOLERANCE), round(y[1], SHAPEFILE_TOLERANCE)) G.add_edge(x, y, weight=data[weight_field]) # get the building nodes and coordinates iterator_nodes = building_nodes_graph.nodes(data=True) terminal_nodes_coordinates = [] terminal_nodes_names = [] for coordinates, data in iterator_nodes._nodes.items(): building_name = data['Name'] if building_name in disconnected_building_names: print( "Building {} is considered to be disconnected and it is not included" .format(building_name)) else: terminal_nodes_coordinates.append( (round(coordinates[0], SHAPEFILE_TOLERANCE), round(coordinates[1], SHAPEFILE_TOLERANCE))) terminal_nodes_names.append(data['Name']) # calculate steiner spanning tree of undirected potential_network_graph try: mst_non_directed = nx.Graph(steiner_tree(G, terminal_nodes_coordinates)) nx.write_shp(mst_non_directed, output_network_folder ) # need to write to disk and then import again mst_nodes = gdf.from_file(path_output_nodes_shp) mst_edges = gdf.from_file(path_output_edges_shp) except: raise ValueError( 'There was an error while creating the Steiner tree. ' 'Check the streets.shp for isolated/disconnected streets (lines) and erase them, ' 'the Steiner tree does not support disconnected graphs. ' 'If no disconnected streets can be found, try increasing the SHAPEFILE_TOLERANCE in cea.constants and run again. ' 'Otherwise, try using the Feature to Line tool of ArcMap with a tolerance of around 10m to solve the issue.' ) # POPULATE FIELDS IN NODES pointer_coordinates_building_names = dict( zip(terminal_nodes_coordinates, terminal_nodes_names)) def populate_fields(coordinate): if coordinate in terminal_nodes_coordinates: return pointer_coordinates_building_names[coordinate] else: return "NONE" mst_nodes['coordinates'] = mst_nodes['geometry'].apply( lambda x: (round(x.coords[0][0], SHAPEFILE_TOLERANCE), round(x.coords[0][1], SHAPEFILE_TOLERANCE))) mst_nodes['Building'] = mst_nodes['coordinates'].apply( lambda x: populate_fields(x)) mst_nodes['Name'] = mst_nodes['FID'].apply(lambda x: "NODE" + str(x)) mst_nodes['Type'] = mst_nodes['Building'].apply(lambda x: 'CONSUMER' if x != "NONE" else "NONE") # do some checks to see that the building names was not compromised if len(terminal_nodes_names) != (len(mst_nodes['Building'].unique()) - 1): raise ValueError( 'There was an error while populating the nodes fields. ' 'One or more buildings could not be matched to nodes of the network. ' 'Try changing the constant SNAP_TOLERANCE in cea/constants.py to try to fix this' ) # POPULATE FIELDS IN EDGES mst_edges.loc[:, 'Type_mat'] = type_mat_default mst_edges.loc[:, 'Pipe_DN'] = pipe_diameter_default mst_edges.loc[:, 'Name'] = ["PIPE" + str(x) for x in mst_edges.index] if allow_looped_networks: # add loops to the network by connecting None nodes that exist in the potential network mst_edges, mst_nodes = add_loops_to_network(G, mst_non_directed, mst_nodes, mst_edges, type_mat_default, pipe_diameter_default) # mst_edges.drop(['weight'], inplace=True, axis=1) if create_plant: if optimization_flag == False: building_anchor = calc_coord_anchor(total_demand_location, mst_nodes, type_network) mst_nodes, mst_edges = add_plant_close_to_anchor( building_anchor, mst_nodes, mst_edges, type_mat_default, pipe_diameter_default) else: for building in plant_building_names: building_anchor = building_node_from_name(building, mst_nodes) mst_nodes, mst_edges = add_plant_close_to_anchor( building_anchor, mst_nodes, mst_edges, type_mat_default, pipe_diameter_default) # GET COORDINATE AND SAVE FINAL VERSION TO DISK mst_edges.crs = crs_projected mst_nodes.crs = crs_projected mst_edges['length_m'] = mst_edges['weight'] mst_edges[['geometry', 'length_m', 'Type_mat', 'Name', 'Pipe_DN']].to_file(path_output_edges_shp, driver='ESRI Shapefile') mst_nodes[['geometry', 'Building', 'Name', 'Type']].to_file(path_output_nodes_shp, driver='ESRI Shapefile')