I have a connected network consisting of lines (edges) and points (nodes). I must connect a subset of these nodes together along the shortest path.
A zoomed-in image of my network is below. The red dots (end points) must all be interconnected to each other following the shortest path. For example, to connect one end point to another, I would need to travel down a green line (connecting line) and along one or more grey lines (path lines) and back up another green line (connecting line) just to connect those two red end points together.
As you can see in the following image, I have accomplished this using the following algorithm.
def optimize_fpp_with_mst_addresses_only(g):
counter = 0
# Extract address nodes
address_nodes = [node for node, data in g.nodes(data=True) if data.get('type') == 'address']
num_address = len(address_nodes)
print(f'number of addresses: {num_address}')
# Create a complete weighted graph with addresses as nodes
complete_graph = nx.Graph()
for i, u in enumerate(address_nodes):
counter += 1
for j, v in enumerate(address_nodes):
if i < j: # Avoid duplicate edges and self-loops
path_length = nx.shortest_path_length(g, source=u, target=v, weight='length')
complete_graph.add_edge(u, v, weight=path_length)
print(f'Weighted graph construction: {float(counter/num_address)*100}% complete..')
counter = 0
# Compute MST on the complete graph
print(f'computing mst on complete graph..')
mst = nx.minimum_spanning_tree(complete_graph, weight='weight')
#mst = nx.algorithms.approximation.steinertree.steiner_tree(complete_graph,
# terminal_nodes=address_nodes, weight='weight', method = 'mehlhorn')
num_edges = len(mst.edges())
# Reconstruct the MST in the original graph
expanded_mst = nx.Graph()
for u, v in mst.edges():
counter += 1
path = nx.shortest_path(g, source=u, target=v, weight='length')
for idx in range(len(path) - 1):
node_u, node_v = path[idx], path[idx+1]
if not expanded_mst.has_edge(node_u, node_v):
expanded_mst.add_edge(node_u, node_v, **g[node_u][node_v])
print(f'Reconstructed MST graph: {float(counter/num_edges)*100}% complete..')
print(f'finished!')
return expanded_mst
PROBLEM: Now the hard part. I am tasked with optimizing the network and have the freedom to cut end points from the network if it yields a more optimal value. I believe the value I am trying to minimize is:
Z = sum edge lengths along path / number of connected addresses in path
As @FelixIP pointed out, there is an optimal solution and it would be a single address node not connected to anything:
Z = 0/1
I want to avoid that situation without providing hard coded constraints like minimum number of addresses to connect = 10.
If I added an attribute (N) to every node, so that every node that is an endpoint of the green lines, but is not a red address point gets a value of 1 and all other nodes get a value of 0, perhaps I can ensure more than one address is connected by modifying my function to maximize:
Z = sum N for every node in path connecting addresses / sum length edges in path connecting addresses
If I maximize that value, I am instructing the optimization algo to find the subnetwork connecting addresses so that the number of addresses connected to the network is as many as possible, while the total length of the subnetwork is as small as possible.
If this is right, then how might I implement it?