1

I am implementing an algorithm to solve the 8-queens problem: https://en.wikipedia.org/wiki/Eight_queens_puzzle

I have run into a problem when trying to iterate through the population and create a new population by doing some crossover. The problem is inside the choice function from numpy.random.

My intention is to assign population to newPopulation (line 50), empty newPopulation (line 53), and then the next iteration appends a new 100 children to newPopulation.

It is complaining that the lists inputted to the choice() do not have the same size, even though I print the len() of each right before the loop and it shows they are both 100?

Full Error:

Traceback (most recent call last):
  File "test.py", line 44, in <module>
    choice1 = choice(population, p=normalFitness)
  File "mtrand.pyx", line 1141, in mtrand.RandomState.choice
ValueError: 'a' and 'p' must have same size

Here is my program:

  1 import queensState as qs
  2 import numpy as np
  3 import random
  4 from numpy.random import choice
  5 
  6 def cross(state1,state2):
  7     return qs.State([state1.state[0],state1.state[1],state1.state[2],state1.state[3],state2.state[4],    state2.state[5],state2.state[6],state2.state[7]])
  8 
  9 #create the initial population
 10 population = []
 11 newPopulation = []
 12 normalFitness = []
 13 fitness=np.zeros((100,),dtype=int)
 14 
 15 #step through each state and give
 16 #random values to each index in the array
 17 for i in range(100):
 18     x = []
 19     for h in range(8):
 20         d = random.randint(1,8)
 21         x.append(d)
 22     newState = qs.State(x)
 23     #append the current state to the population with
 24     #the fitness(hash) and the state array
 25     population.append(newState)
 26 
 27 
 28 for i in range(100):
 29     totalFitness = 0
 30     for n in range(100): #calculate total fitness
 31         totalFitness+=population[n].fitness
 32 
 33     fitness[i]=totalFitness
 34 
 35     #initialize the normalized fitness array
 36     for x in range(100):
 37         normalFitness.append(population[x].fitness/totalFitness)
 38 
 39     print("poplen: ",len(population)," fitlen: ",len(normalFitness)) #this prints "poplen: 100 fitlen: 100"
 40     for x in range(100): #this loop is solely for testing
 41         print("Pop: ",population[x].state," normalfit: ", normalFitness[x])
 42 
 43     for x in range(100):
 44         choice1 = choice(population, p=normalFitness)
 45         choice2 = choice(population, p=normalFitness)
 46         child = cross(choice1,choice2)
 47         newPopulation.append(child)
 48         #print("State1: ", choice1.state, " State2: ",choice2.state, "Cross: ", child.state)
 49 
 50     population = newPopulation
 51     for x in range(100):
 52         print(population[x].state)
 53     normalFitness=[]
 54     print(len(normalFitness))
 55 
 56 print(fitness)

I thought it could be because I assigned normalFitness list to an empty list instead of using del. If I change line 53 to: del normalFitness[:] it has the same behavior.

Here is the queensState code if needed:

  1 import numpy as np
  2 
  3 class State:
  4     state = np.zeros(8)
  5     fitness=0
  6     normalizedFitness=0
  7 
  8     def __init__(self, state):
  9         self.state = state
 10         self.fitness = hash(self)
 11 
 12     #the hash is used to find the fitness
 13     #the fitness is defined by the number of non attacking queens
 14     def __hash__(self):
 15         count = 0
 16         attackingQueens = [False,False,False,False,False,False,False,False]
 17         for i in range(0,7):
 18             x1 = i+1
 19             y1 = self.state[i]
 20             for j in range(i+1,8):
 21                 x2 = j+1
 22                 y2 = self.state[j]
 23                 if y1==y2 or abs((x1-x2))==abs((y1-y2)):
 24                     attackingQueens[i]=True
 25                     attackingQueens[j]=True
 26         for attacking in attackingQueens:
 27             if not attacking:
 28                 count+=1
 29         self.fitness=count
 30         return count

Any thoughts are greatly appreciated!

Thank you.

Colin Gregory
  • 55
  • 1
  • 1
  • 5
  • The error is raised by `mtrand.RandomState.choice`, not `np.random.choice`. – hpaulj May 21 '19 at 01:14
  • Looks like `population` goes through the 100 loop just once, but `normalFitness` goes through it multiple times (for each `i`). Do you need to reset it to [] at line 35? – hpaulj May 21 '19 at 01:20
  • `population = newPopulation` isn't a copy. You have two names for the same list, a list you're appending to. `population` is growing. – user2357112 May 21 '19 at 01:21
  • (Also if you wanted to reset `newPopulation` to an empty list every time through the loop, well, you're not doing that. Dunno whether you wanted to, though.) – user2357112 May 21 '19 at 01:22
  • @user2357112 the only time I am appending to population is when I assign the initial random list of states on line 17. My intention is to set the population to the newPopulation after each iteration and then empty newPopulation so I can append new children states in the next iteration. – Colin Gregory May 21 '19 at 02:30
  • @ColinGregory: You *think* that's the only time you're appending to `population`, but when `population` and `newPopulation` are two references to the same list, `newPopulation.append(child)` is appending to the same list referenced by `population`. – user2357112 May 21 '19 at 02:38
  • "because I empty newPopulation on line 53" - reread line 53. See what you're actually doing there. – user2357112 May 21 '19 at 02:39
  • @hpaulj ahhh yes that is it. Thank you. – Colin Gregory May 21 '19 at 02:41
  • @user2357112 oh ok. So I need to do ```population = newPopulation.copy()``` – Colin Gregory May 21 '19 at 02:45
  • Well, you still wouldn't be emptying `newPopulation` if that was all you changed. – user2357112 May 21 '19 at 02:46
  • @user2357112 ok I get it now! thank you so much. I apologize it took me a minute to realize I wasn't emptying ```newPopulation``` and only ```normalFitness``` – Colin Gregory May 21 '19 at 02:55

0 Answers0