1 year ago

#308799

test-img

Hermit

Python Temperamentally Accessing Global Object from within a Function (which references that object from within another)

(Edit: Fundamentally my problem is that python is sometimes creating new instances of an object x,which I accessed by another object y, accessed by z instead of editing the original x directly. x and y both belong to a list of global variables, but when I access y via z to access x via y on a subsequent iteration of my recursive algorithm, its information isn't always correctly updated.)

Introduction

I'm writing a recursive function to emulate this version of Dijkstra's algorithm (Problem 2) with input from a CSV file. I have two globals Branches [] and Nodes [] to store all branch and node python objects. (I'll put how everything is initialized below).

When I attempt to change the set of the destination node of my branch from within my function, python generates a new object. It's not that it cannot access global Branches, because it does function correctly when the start node of the branch I am accessing is the same as the origin.

Screenshot of algorithm output at various times, one of which is correct and one of which is not

Update: I tried searching through the global list for a node with the label matching the one I wanted to change, but while this correctly changed the set of the global none of the branches referencing that node recognized the change

Branch does not recognize change

def dijkstra_algorithm(node_being_investigated, origin_node, destination_node):
...
for branch in shortest_route.requisite_branches:

        # finding the new branch added to the route and adding it to set I and its destination to set A
        if branch not in branches_in_set_I:

            print(f"adding the new branch {branch.info()} to set I")
            print(f"Adding the new node {branch.destination.info()} to set A")
            print(f"The New Node: {branch.destination} ")

            branch.set = "I"
            branch.destination.set = "A"           
    
    # a redundancy I added just in case the for loop was somehow messing with things
    shortest_route.requisite_branches[0].destination.set="A"

    if destination in obtain_nodes_of_set("A"):
        return shortest_route
    else:
        return dijkstra_algorithm(shortest_route.requisite_branches[0].destination, origin, destination)

NB: There is one other class Routes which stores a list of branches and their cumulative time. I suspect that since this is the only place some operation is performed on a node or branch, the problem is linked to this somehow. However, since I'm not sure how, here's a small table of contents of all the code snippets I've attached. I can send more (or even the whole file) if need be.

The Code Below

  • the way I define the Node and Branch classes
  • the initialization of everything from the CSVs
  • the way I define the Route class
  • the search_to_origin function which finds the overall length of various Route options
  • the piece of the dijkstra_algorithm function which finds the shortest_route inputted to this piece of the algorithm

Defining the Node and Branch classes

class Node:
def __init__(self, label, set):
    self.label = label
    self.set = set and set or "C"
    print(f"\n\n!!!creating new node object with label {self.label}!!!\n\n")

def info(self):
    return f"Set {self.set}: [[{self.label}]]"

and:

class Branch:
def __init__(self, origin, destination, duration, set):
    self.origin = origin
    self.destination = destination
    self.duration = duration
    self.set = set and set or "III"

def info(self):
    return f"Set {self.set}:{self.origin.info()} -> {self.destination.info()} ({self.duration})"

Initializing the global Lists from the CSVs

From what I've checked by printing out the object IDs this seems to be working correctly, but since I'm not actually sure where the problem lies, I'm including it just in case

with open('nodes.csv') as csv_file:
    csv_reader = csv.DictReader(csv_file)

    for row in csv_reader:
        Nodes.append(Node(row["LABEL"], 99999, "C"))

# here all the roads are read into the global list of branches
with open('roads.csv') as csv_file:
    csv_reader = csv.DictReader(csv_file)

    for row in csv_reader:

        branch_info = {}

        # getting the actual node objects to attach
        for node in Nodes:

            if node.label == row["ORIGIN"]:
                branch_info['origin'] = node

            if node.label == row["DESTINATION"]:
                branch_info['destination'] = node

        Branches.append(Branch(branch_info['origin'], branch_info['destination'], int(row["DURATION"]), "III"))

The Route Class and the Main Function Operating on It

There is a sub function called by the overall algorithm which traces the route from a list of branches back to a specified origin node object. Afterwards it returns a list of possible_routes to the main dijkstra function, for that function to decide which is quickest.

Route Class Initialization

class Route:
    def __init__(self, branch_list, duration):
        self.requisite_branches = branch_list
        self.duration = duration

    def extend_route(self, branch):
        self.requisite_branches.append(branch)
        self.duration += branch.duration

The function determining routes to the origin

Node that this calls a utility function find_attached_branches which searches for branches with a node as its origin or destination from a specific set.

def search_to_origin(origin, branches, route):

    print("\n Searching to origin...")

    possible_routes = []

    # for each of the branches being investigated
    for branch in branches:

        # if this branch has not already been investigated in this route
        if branch not in route.requisite_branches:

            # this is a new individual route being investigated
            new_route = copy.deepcopy(route)
            new_route.extend_route(branch)

            # if the start node has been found this is a possible route
            if branch.origin == origin:

                print("This branch leads to the origin")
                possible_routes.append(new_route)

            # if the start node has not been found search for branches preceding this one
            else:

                branches_to_this_node = find_attached_branches(branch.origin, 'destination, "II")
                branches_to_this_node.extend(find_attached_branches(branch.origin, 'destination, "I"))

                if len(branches_to_this_node) != 0:
                    print("this branch does not lead to the origin")
                    route_to_start = search_to_origin(origin, branches_to_this_node, new_route)
                    possible_routes.extend(route_to_start)


    # return the lengths and requisite branches found
    return possible_routes

Finding the Quickest Route to the Origin From a Given Node in B

This portion of the algorithm is done just before the one you see in the introduction section. The way it functions should be independent of whether the shortest route only consists of one branch, but this doesn't seem to be the case. It goes through the following steps:

  1. Looks for all the nodes now in set B.
  2. For each of these nodes it finds all the branches attached to each node in set B.
  3. For all of those branches it looks for routes back to the origin.
  4. From all of the resulting routes it determines the shortest_route
global Nodes

    # get all the nodes in set B
    nodes_in_set_B = obtain_nodes_of_set("B")

    # get all branches in sets I or II 
    global Branches
    branches_in_I_or_II = []
    for branch in Branches:
        if branch.set != "III": branches_in_I_or_II.append(branch)

    # the shortest route found from one of the nodes of set B. Initialized as a large empty route
    shortest_route  = Route([], 99999)

    if nodes_in_set_B is not None:

        for node in nodes_in_set_B:

            # the branches under consideration are only those to this node in set B
            branches_under_consideration = []
            for branch in branches_in_I_or_II:
                if branch.destination == node: branches_under_consideration.append(branch)

            possible_routes = search_to_origin(origin, branches_under_consideration, Route([], 0))

            # finding the possible route of minimum length
            for route in possible_routes:
                if route.duration < shortest_route.duration: 
                    shortest_route = route

python

pointers

dijkstra

0 Answers

Your Answer

Accepted video resources