Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Indexing error with an empty sublist

I am trying to implement a tree data structure. One of the methods in my tree object associates nodes with coordinates whilst mapping the children to the parent nodes & vice-verse.

Initially I gave each node a list of children which turned out to be incorrect. So to remedy that I decided to just initialize nodes with an empty list and that's where the trouble started.

First the code that works...

    def treeSetUp(self):

        '''Set tree's initial structure
        For each level in treeArray
        [level, leaf, [node number, parent, full, [coordinates], [centroid], [children]], [node number, leaf, full, [coordinates], [centroid], [children]], ...]'''


        # Create root level & node    
        leaf = False    
        numChildren = (self.dims[0] * self.dims[1] * self.dims[2])/self.diameters[0]**3 # This maybe whay it only works for cubes
        children = [(kids) for kids in xrange(numChildren)]
        rootCoords = np.array([[0, 0, 0], [0, self.dims[0], 0], [self.dims[0], 0, 0], [self.dims[0], self.dims[1], 0], [0, 0, self.dims[2]], [0, self.dims[1], self.dims[2]], [self.dims[0], 0, self.dims[2]], [self.dims[0], self.dims[1], self.dims[2]]])
        xCentroid = np.sum(rootCoords[:,0])/8.0
        yCentroid = np.sum(rootCoords[:,1])/8.0
        zCentroid = np.sum(rootCoords[:,2])/8.0
        rootCentroids = [xCentroid, yCentroid, zCentroid]
        rootNode = [0, None, False, rootCoords, rootCentroids, copy.copy(children)]

        treeArray = []
        treeArray.append([0, leaf, [rootNode]])
        allCoordinates = []
        allCoordinates.append(rootCoords)
        allCentroids = []
        allCentroids.append(rootCentroids)

        for idx in xrange(self.depth):

            # self.coordGenerator splits a given domain into cubes & returns the
            # coordinates of each cube as well as its associated centroid
            # This is done at different resolutions at different levels
            levelCoordinates, levelCentroid = self.coordGenerator(idx+1)
            allCoordinates.append(levelCoordinates)
            allCentroids.append(levelCentroid)

            nodeCount = 0            
            if idx == self.depth-1:
                leaf = True 

            # Generate level's nodes            
            newNodes = []
            for parentNode, node in enumerate(treeArray[idx][2]):

                # Generate nodes's child list                
                children = []
                for nodeNumber, child in enumerate(node[5]):

                    # Gereate child list                    
                    if leaf:
                        children = [None]
                    else:
                        numChildren = (self.diameters[idx]**3)/(self.diameters[idx+1]**3)
                        children = [(kids + nodeCount*numChildren) for kids in xrange(numChildren)]

                    # Assign coordinates to level 1 nodes else generate placeholders      
                    if idx == 0:
                        nodeCoords = levelCoordinates[nodeNumber]
                        nodeCentroid = levelCentroid[nodeNumber]
                    else:
                        nodeCoords = []
                        nodeCentroid = []

                    newNodes.append([nodeCount, parentNode, False, copy.deepcopy(nodeCoords), copy.deepcopy(nodeCentroid), copy.copy(children)])
                    nodeCount += 1

            newLevel = [idx+1, leaf, copy.deepcopy(newNodes)]
            treeArray.append(newLevel)

        # Operation find parents
        for level in xrange(1, self.depth):
            for childNode, centroid in enumerate(allCentroids[level+1]):

                for node in treeArray[level][2]:

                    # bounding box of parent node
                    boundary = node[3]
                    xBound = [np.min(boundary[:,0]), np.max(boundary[:,0])]
                    yBound = [np.min(boundary[:,1]), np.max(boundary[:,1])]
                    zBound = [np.min(boundary[:,2]), np.max(boundary[:,2])]

                    # Is child's centroid in parents ounding box?
                    if (xBound[0] <= centroid[0] <= xBound[1]) and (yBound[0] <= centroid[1] <= yBound[1]) and (zBound[0] <= centroid[2] <= zBound[1]):
                        treeArray[level+1][2][childNode][1] = node[0]       # Add parent node to child
                        treeArray[level+1][2][childNode][3] = allCoordinates[level+1][childNode]
                        treeArray[level+1][2][childNode][4] = centroid
#                        treeArray[level][2][node[0]][5].append(childNode)   # Add child to parents list of children
                        break

And now the code that doesnt...

   def treeSetUp(self):

        '''Set tree's initial structure
        For each level in treeArray
        [level, leaf, [node number, parent, full, [coordinates], [centroid], [children]], [node number, leaf, full, [coordinates], [centroid], [children]], ...]'''


        # Create root level & node    
        leaf = False    
        numChildren = (self.dims[0] * self.dims[1] * self.dims[2])/self.diameters[0]**3 # This maybe whay it only works for cubes
        children = [(kids) for kids in xrange(numChildren)]
        rootCoords = np.array([[0, 0, 0], [0, self.dims[0], 0], [self.dims[0], 0, 0], [self.dims[0], self.dims[1], 0], [0, 0, self.dims[2]], [0, self.dims[1], self.dims[2]], [self.dims[0], 0, self.dims[2]], [self.dims[0], self.dims[1], self.dims[2]]])
        xCentroid = np.sum(rootCoords[:,0])/8.0
        yCentroid = np.sum(rootCoords[:,1])/8.0
        zCentroid = np.sum(rootCoords[:,2])/8.0
        rootCentroids = [xCentroid, yCentroid, zCentroid]
        rootNode = [0, None, False, rootCoords, rootCentroids, copy.copy(children)]

        treeArray = []
        treeArray.append([0, leaf, [rootNode]])
        allCoordinates = []
        allCoordinates.append(rootCoords)
        allCentroids = []
        allCentroids.append(rootCentroids)

        for idx in xrange(self.depth):

            # self.coordGenerator splits a given domain into cubes & returns the
            # coordinates of each cube as well as its associated centroid
            # This is done at different resolutions at different levels
            levelCoordinates, levelCentroid = self.coordGenerator(idx+1)
            allCoordinates.append(levelCoordinates)
            allCentroids.append(levelCentroid)

            nodeCount = 0            
            if idx == self.depth-1:
                leaf = True  

            # Generate level's nodes            
            newNodes = []
            for parentNode, node in enumerate(treeArray[idx][2]):

                # Generate nodes's child list                
                children = []
                for nodeNumber, child in enumerate(node[5]):

                    # Gereate child list                    
                    if leaf:
                        children = [None]
                    else:
                        numChildren = (self.diameters[idx]**3)/(self.diameters[idx+1]**3)
                        children = [(kids + nodeCount*numChildren) for kids in xrange(numChildren)]

                    # Assign coordinates to level 1 nodes else generate placeholders      
                    if idx == 0:
                        nodeCoords = levelCoordinates[nodeNumber]
                        nodeCentroid = levelCentroid[nodeNumber]
                    else:
                        nodeCoords = []
                        nodeCentroid = []

                    newNodes.append([nodeCount, parentNode, False, copy.deepcopy(nodeCoords), copy.deepcopy(nodeCentroid), []]) #copy.copy(children)])
                    nodeCount += 1

            newLevel = [idx+1, leaf, copy.deepcopy(newNodes)]
            treeArray.append(newLevel)

        # Operation find parents
        for level in xrange(1, self.depth):
            for childNode, centroid in enumerate(allCentroids[level+1]):

                for node in treeArray[level][2]:

                    # bounding box of parent node
                    boundary = node[3]
                    xBound = [np.min(boundary[:,0]), np.max(boundary[:,0])]
                    yBound = [np.min(boundary[:,1]), np.max(boundary[:,1])]
                    zBound = [np.min(boundary[:,2]), np.max(boundary[:,2])]

                    # Is child's centroid in parents ounding box?
                    if (xBound[0] <= centroid[0] <= xBound[1]) and (yBound[0] <= centroid[1] <= yBound[1]) and (zBound[0] <= centroid[2] <= zBound[1]):
                        treeArray[level+1][2][childNode][1] = node[0]       # Add parent node to child
                        treeArray[level+1][2][childNode][3] = allCoordinates[level+1][childNode]
                        treeArray[level+1][2][childNode][4] = centroid
#                        treeArray[level][2][node[0]][5].append(childNode)   # Add child to parents list of children
                        break

The only difference being that instead of supplying the erroneous list of child nodes, I supplied an empty list.

The error it gives..

treeArray[level+1][2][childNode][1] = node[0]       # Add parent node to child IndexError: list index out of range

So why would adding an empty list instead of an occupied list change indexing?

I dont have the privilege to share the rest of the code unfortunately, I have tried to replicate this error in a simplified code but so far no luck.

I suspect that there is something obvious here but after a day of staring at it with incredulity, well its making less sense.

like image 761
DrBwts Avatar asked Nov 26 '25 20:11

DrBwts


1 Answers

I believe if you print

treeArray[level+1][2][childNode]

right before the line where the error occurs, you will find that it is [] in the code that doesn't work and equal to something else in the code that does work. So when you set ...[childNode][1] to have a value, you're running into the same error you would if you had

a = []
a[1]=1
> IndexError: list assignment index out of range

Whereas in the working code you're seeing something like:

a=[4,6]
a[1]=1
print a
>[4,1]

When you don't send it an empty list, but rather a copy of a child, then when it tries to set that entry, it is successful because that entry is already in the list, it's just changing the value. On the other hand if it's trying to set the [1] entry of an empty list, then it's going to fail because the list entry doesn't exist yet. Think of a[n]=x as being like filing the legal forms required to change the identity of a[n]. The process does not work if you file the forms for a person who does not exist. To actually create a[n] requires some other processes to happen.

You should simplify the code significantly. Rather than having lists of things like: [nodeCount, parentNode, False, copy.deepcopy(nodeCoords), copy.deepcopy(nodeCentroid), []], I think you'll be better off defining an object that contains all of this. It will be easier to debug.

like image 199
Joel Avatar answered Nov 29 '25 10:11

Joel



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!