KEY ERROR AND I HAVE NO IDEA WHY

161 views
Skip to first unread message

Aamina Khurram

unread,
Aug 2, 2023, 4:54:40 PM8/2/23
to networkx-discuss

hi guys,
I've been getting a key error in line 182, in the embed_request function, and I've tried debugging it but to no avail. I've been stuck on this for a week now, and just don't know what to do. Any help would be greatly appreciated!
CODE:
import simpy
import random
import networkx as nx
import matplotlib.pyplot as plt
import numpy as np
import math
import copy

def calculate_distance(node_1, node_2):
    '''
    calculates distance between two nodes.
    inputs: node1, node2
    output: distance
    '''
    position1 = node_positions[node_1]
    position2 = node_positions[node_2]
    # Calculate the Euclidean distance between the two positions
    distance = math.sqrt((position1[0] - position2[0])**2 + (position1[1] - position2[1])**2)
    return distance

def calculate_ellipse_points(cx, cy, a, b, num_points): # (center_x_coord, center_y_coord, major axis, minor axis, num of points)
    # determine the points along the satellites' elliptical path
    points = []
    angle_increment = 2 * math.pi / num_points

    for i in range(num_points):
        theta = i * angle_increment
        x = cx + a * math.cos(theta)
        y = cy + b * math.sin(theta)
        points.append((x, y))

    return points

def update_links(env):
    global communication_range, gs_capacity, visibility_tables
    # Iterate over all pairs of satellite and terrestrial nodes (sat-gs links)
    for node1 in G.nodes():
        if node1.startswith('Satellite'):
            for node2 in nodes:
                if (node2 != node1) and (node2.startswith('Node')):
                    distance = calculate_distance(node1, node2)
                    # print("distance bw",node1," and ",node2," : ",distance)
                    # Check if the distance is within the communication range
                    if distance <= communication_range:
                        # If the edge does not exist, add it
                        if not G.has_edge(node1, node2):
                            # print("adding a ground-air link")
                            G.add_edge(node1, node2)
                            G.edges[node1, node2]['capacity'] = simpy.Container(env, init=1000, capacity=1000)
                    else:
                        # If the edge exists, remove it
                        if G.has_edge(node1, node2):
                            G.remove_edge(node1, node2)
                           
def move_satellites(graph):
    global satellite_nodes, satellite_path, current_satellite_positions, satellite_positions, node_positions

    for satellite in satellite_nodes:
            current_pos = graph.nodes[satellite]['pos']
            #print('current pos of ',satellite,' is ',current_pos)
            next_pos = satellite_path[(satellite_path.index(current_pos) + 1) %len(satellite_path)] # next position in orbit
            #print('next pos of ',satellite,' is ',next_pos)
            current_satellite_positions[satellite] = next_pos
            graph.nodes[satellite]['pos'] = next_pos  # Update the position of the node
    satellite_positions = list(current_satellite_positions.values())
    node_positions = {**gateway_positions, **current_satellite_positions}
    update_links(env) #tear down old ground to air links, and make new ones, as needed (done every time satellites move)
   

def simulate_satellite_motion(env, time_step, topology_change_event):
    global G, num_of_iter
    # while True:
    for i in range(num_of_iter):
        # print(time_step)
        # Wait for the predetermined interval before changing the topology
        yield env.timeout(time_step)

        # Perform the topology update
        move_satellites(G)

        # Signal that the topology has changed
        topology_change_event.succeed()
        topology_change_event = env.event()
        print('topology has changed at time'+str(env.now))

def store_network_changes():
    global G, num_of_iter, topologies
    topologies.append(G) # storing original topology
    # Create a deep copy of the original topology
    og_topology_copy = copy.deepcopy(G)
    for i in range(num_of_iter):
        move_satellites(og_topology_copy)
        topologies.append(og_topology_copy)
    print(topologies)

def generate_traffic_requests(env, graph, request_store, runtime):
    global traffic_requests
    id = 0
    while True:
    # for i in range(5):
        # Generate a new request at random intervals following a Poisson distribution
        # interval = np.random.exponential(scale=60)  # Scale parameter of 60 seconds (mean interval)
        interval = random.randint(30,300)
        # interval = 30
        yield env.timeout(interval)

        source = random.choice(list(graph.nodes))
        dest = random.choice(list(graph.nodes))

        while source == dest:
            dest = random.choice(list(graph.nodes))

        req = (source, dest)
        required_rate = random.randint(5, 800)
        # required_rate = 500
        time_req = random.randint(60, 480)  # Random time requirement between 1 min and 8 minid+=1
        # time_req = (i+1)*100
        while time_req > (runtime - env.now):
            time_req = random.randint(60, 480) # make sure request duration doesn't go past simulation runtime(for requests generated
                                               # later in the simulation)
        id+=1
        # requests = {}
        # with threading.Lock():
        #     requests[req] = {'required_rate': required_rate, 'time_req': time_req, 'new_request':True}
        #     traffic_requests.update(requests)

        # Signal that a new request is generated
        request_store.put({'id':id, 'req': req, 'required_rate': required_rate, 'time_req': time_req})
        # request_event = env.event()
        print('request GENERATED at time '+str(env.now) + ', requesting network_bandwidths for '+str(time_req)+' sec(id:'+str(id)+')')
        # yield from embed_request(env, graph, req, required_rate, time_req, time_step, id)
        # yield from embed_request(env, graph, req, required_rate, time_req, time_step, id)

def embed_request(env, graph, request, req_rate, req_time, time_step, id):
    global request_status, count_of_reqs_embedded

    # Rest of the code for handling the request remains the same
    source, destination = request
    # Find all simple paths between the source and destination nodes
    paths = nx.all_simple_paths(graph, source, destination)
   
    # Sort the paths by length (shortest to longest)
    sorted_paths = sorted(paths, key=len)
    remaining_capacity_in_network = 0
    for edge in list(graph.edges):
        remaining_capacity_in_network += graph.edges[edge]['capacity'].level
    print('time: '+str(env.now)+'remaining_capacity_in_network'': '+ str(remaining_capacity_in_network))
   
    # Iterate over the sorted paths
    for path in sorted_paths:
        # Check if the path has sufficient capacity
        # for e in zip(path, path[1:]):
        #     print(e)

        print('req_rate: ', req_rate)
        if all(graph.edges[e]['capacity'].level >= req_rate for e in zip(path, path[1:])):
            # Check if the path will exist for the duration of the request
            if path_viable(env, path, source, destination, req_time, time_step):
                count_of_reqs_embedded += 1
                # with network_bandwidth.request() as req:
                # Deduct the capacity from the edges in the path
                for e in zip(path, path[1:]):
                    graph.edges[e]['capacity'].get(req_rate)
                remaining_capacity_in_network = 0
                for edge in list(graph.edges):
                    remaining_capacity_in_network += graph.edges[edge]['capacity'].level
                # yield req
                print('- - - - - - - -- - - - - - - - - - - - - - -- - - - - - - -path just after yield req: ',path,'id: ', id)
                request_status[request] = True
                print('request '+ ', requesting resources for '+str(req_time)+' sec successfully embedded at time '+str(env.now)+'(id:'+str(id)+')')
                print('time: '+str(env.now)+'remaining_capacity_in_network when resources have just been given to '+str(id)+': '+ str(remaining_capacity_in_network))
                yield env.timeout(req_time)
                print('- - - - - - - -- - - - - - - - - - - - - - -- - - - - - - -path just after yield env.timeout(req_time): ',path,'id: ',id)

                # Release the network_bandwidths used
                for e in zip(path, path[1:]):
                    print(path)
                    print('e:',e)
                    # for e in graph.edges:
                    #     print(e)
                    # G.edges[e]['capacity'].put(req_rate)
                    graph.edges[e]['capacity'].put(req_rate)
                print('id of req which is returning resurces: ',id)
                print('network_bandwidths successfully regained at time '+str(env.now)+'(id:'+str(id)+')')
                remaining_capacity_in_network = 0

                for edge in list(graph.edges):
                    remaining_capacity_in_network += graph.edges[edge]['capacity'].level
                return True
        else:
            request_status[request] = False
            print('request embedding unsuccessful'+ '(which was requesting network_bandwidths for '+str(req_time)+' sec)'+'(id:'+str(id)+')')
            return False
   
def handle_requests(env, graph, request_store, time_step):
    while True:
        # Wait for a new request to be generated
        request_info = yield request_store.get()

        # Extract the request details
        req = request_info['req']
        required_rate = request_info['required_rate']
        time_req = request_info['time_req']
        id = request_info['id']

        print('HANDLING request'+ ', requesting network_bandwidths for '+str(time_req)+' sec at time '+str(env.now)+'(id:'+str(id)+')')
        # yield from embed_request(env, graph, req, required_rate, time_req, time_step, id)

        remcap = 0
       
        for edge in list(graph.edges):
            remcap += graph.edges[edge]['capacity'].level
        print(env.now,' rem capacity in handle_req(bef embed_req runs for ',id,'): ',remcap)

        # Embed the request
        env.process(embed_request(env, graph, req, required_rate, time_req, time_step, id))

def path_exists(path, source, destination, next_change_time, time_step):
    # returns true if the path provided exists in the network topology at next_change_time
    global topologies
    index = next_change_time / time_step
    graph = topologies[int(index-1)]
    paths = nx.all_simple_paths(graph, source, destination) # Find all simple paths between the source and destination nodes
    if path in paths: # check if the path chosen will exist in the next version of the topology
        return True
    return False

def path_viable(env, path, source, destination, req_duration, time_step):
    survives_topo_changes =  True
    time_to_next_top_change = time_step - (env.now % time_step)
    print('time_to_next_top_change: ',time_to_next_top_change)
    # determine topology changes that will occur during the duration of the request
    while time_to_next_top_change < req_duration:
        # determine if the current path will exist in all of those changed topologies
        next_change_time = env.now + time_to_next_top_change
        print('next_change_time: ', next_change_time)
        if path_exists(path, source, destination, next_change_time, time_step) == False:
           survives_topo_changes = False
           break
        req_duration -= time_to_next_top_change
        current_time = next_change_time
        time_to_next_top_change = time_step - (current_time % time_step)
    if survives_topo_changes == False:
        return False
    else:
        print('========= path viable =======')
        return True



def initialize_simulation(env):
    global num_points_along_orbit, gateway_positions, satellite_nodes, current_satellite_positions, satellite_path, satellite_positions, node_positions, total_capacity, satellite_capacity, ground_capacity, G
    gateway_positions = {
        'Node1': (33.7, -91.13),
        'Node2': (41.20, -85.9),
        'Node3': (50.06, -91.63)
    }
    # satellite_path = calculate_ellipse_points(center_x, center_y, a, b, num_points)
    # leo semi-major axis: 8413 km, major axis: 2*semi-major=16826, after scaling down, major axis = 16.826, minor axis = 8.413, num_points = 20
    satellite_path = calculate_ellipse_points(41.7, -90.7, 16.826, 8.413, num_points_along_orbit) # list of tuples: [(48.7, -90.7), (48.35739561406608, -89.15491502812526),...]

    for satellite in satellite_nodes:# {'satellite1': (48.7, -90.7)...}
        current_satellite_positions[satellite] = satellite_path[satellite_nodes.index(satellite)]

    satellite_positions=list(current_satellite_positions.values())
    node_positions = {**gateway_positions, **current_satellite_positions} # combining dictionaries

    # adding nodes to empty graph
    for i in range(num_of_nodes):
        G.add_node(nodes[i], pos=list(node_positions.values())[i])  # Assign initial positions as node attributes

    # Initial topology
    G.add_edges_from([('Node1','Node3'),('Node2','Node3'),('Node1','Node2'),('Satellite1','Satellite2'),('Satellite2','Satellite3'),('Satellite3','Satellite4'),('Satellite4','Satellite5'),('Satellite6','Satellite7'),('Satellite7','Satellite8'),('Satellite6','Satellite5')])

    # Add SimPy containers for each edge
    for edge in G.edges:
        source, target = edge
        if source.startswith('Node') and target.startswith('Node'):
            # Check if the nodes are ground nodes
            capacity = 5000
        else:
            # For non-ground nodes, set a different capacity
            capacity = 1000
        # Create a SimPy container with the determined capacity
        G.edges[source, target]['capacity'] = simpy.Container(env, init=capacity, capacity=capacity)

        # Check if one node is a ground node and the other is a satellite
        # elif (source.startswith('Node') and target.startswith('Satellite')) or (source.startswith('Satellite') and target.startswith('Node')) or (source.startswith('Satellite') and target.startswith('Satellite')):
        #     capacity = simpy.Container(env, capacity=1000)
        #     G[source][target][capacity].put(1000)
    update_links(env) # Air to ground links added based on communication range
    print('simulation initialised')

def run_simulation(env, graph):
    global num_points_along_orbit
   
    period = 7680 # 7680 sec = 128 min (leo period)
    timestep = period//num_points_along_orbit # time between 2 topology changes
    # Run the simulation for a fixed time
    runtime = timestep * num_of_iter
    # Initialize the topology change event
    topology_change_event = env.event()
    request_event = env.event()
    remaining_capacity_in_network = 0
    for edge in list(graph.edges):
        remaining_capacity_in_network += graph.edges[edge]['capacity'].level
    print('############################################################################### # # # ###cap at start: ',remaining_capacity_in_network)
   
    # Initialize the request store
    request_store = simpy.Store(env)
    print('beginning...')
   
    # Start the handle_requests process in parallel with other code
    env.process(handle_requests(env, graph, request_store, timestep))

    # Start the request generation process
    env.process(generate_traffic_requests(env, graph, request_store, runtime))

    # Start the topology change process
    env.process(simulate_satellite_motion(env, timestep, topology_change_event))

    print('time between topology changes: '+str(timestep)+' sec('+str(round(timestep/60, 2))+' min)')
    print('runtime '+ str(runtime)+' sec('+str(round(runtime/60, 2))+' min)')
    env.run(until=runtime)  # run the simulation
    print('ending...')
   
    remaining_capacity_in_network = 0
    for edge in list(graph.edges):
        remaining_capacity_in_network += graph.edges[edge]['capacity'].level

    print('############################################################################### # # # ###cap at end: ',remaining_capacity_in_network)

communication_range = 7.5 # arbitrary value (scaled down 150 times from 1125 km)
terrestrial_nodes = ['Node1', 'Node2', 'Node3']
satellite_nodes = ['Satellite1', 'Satellite2', 'Satellite3', 'Satellite4', 'Satellite5', 'Satellite6', 'Satellite7', 'Satellite8']
nodes = terrestrial_nodes + satellite_nodes
num_of_nodes = len(nodes)

gateway_positions = {}
current_satellite_positions = {}  # {'satellite1': (48.7, -90.7)...}
num_points_along_orbit = 20
G=nx.Graph()
visibility_tables = {}
topologies=[]
# Store the status of each request for visualization
request_status = {}
count_of_reqs_embedded = 0

traffic_requests={}

num_of_iter = 5 # Number of steps taken along orbit in this simulation
env = simpy.Environment()

initialize_simulation(env)

# store graphs of network at all topology changes
store_network_changes()
run_simulation(env, G)

I'm printing the edge and it exists in the graph(prints successfully), then why the key error?

Sample output + error:
e: ('Node2', 'Satellite5')
Traceback (most recent call last):
  File "d:\integrated satellite networks for 6G\path_viability_test.py", line 182, in embed_request
    graph.edges[e]['capacity'].put(req_rate)
    ~~~~~~~~~~~^^^
  File "D:\Users\AAMINA\AppData\Local\Programs\Python\Python311\Lib\site-packages\networkx\classes\reportviews.py", line 1094, in __getitem__
    return self._adjdict[u][v]
           ~~~~~~~~~~~~~~~~^^^
KeyError: 'Satellite5'

seth....@gmail.com

unread,
Aug 3, 2023, 8:45:54 AM8/3/23
to networkx-discuss
I just did a quick skim through this code, the error means there is no edge in the graph.

The graph and the paths keyword is being mutated in multiple places.
In the function path_exists, change the line 

graph = topologies[int(index-1)]

to something like

graph_temp = topologies[int(index-1)]

and

paths= nx.all_simple_paths(graph source, destination)

to something like

paths_temp = nx.all_simple_paths(graph_temp, source, destination)

This will make the code run without error for some simulations (I don't know if that makes it right or not).
Reply all
Reply to author
Forward
0 new messages