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.