예제 #1
0
 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)
예제 #2
0
 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)
예제 #3
0
    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
예제 #4
0
 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)
예제 #5
0
 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)
예제 #6
0
    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')
예제 #8
0
    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)
예제 #9
0
 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)
예제 #10
0
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
예제 #13
0
 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
예제 #14
0
    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
예제 #15
0
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
예제 #16
0
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)
예제 #18
0
def module3(G, topFeatures):
    sT = steiner_tree(G, topFeatures, weight='weight')
    return sT
예제 #19
0
    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')
예제 #20
0
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)
예제 #21
0
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
예제 #22
0
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
예제 #23
0
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')
예제 #25
0
    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')
예제 #27
0
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
예제 #28
0
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')
예제 #29
0
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')