Why is this tree search implementation causing a performance leak? - python

I'm coding a tree search in a simultaneous move game in python. I'm using python at this stage for ease of development, before rewriting the project in C++. For this reason, I would normally not be too concerned about performance. Originally my code was structured like this
class DecisionNode():
def __init__(self, players):
self.transitions = dict()
self.N = [dict() for _ in players]
self.V = [dict() for _ in players]
self.visited = 0
self.c = 2
self.players = players
def search(self, s, depth=0, verbose=False, mode=None):
if not self.visited: #unexpanded node
score, log = s.rollout()
actions = s.actions()
for acts, n, v in zip(actions, self.N, self.V):
for a in acts:
n[a] = 0
v[a] = 0
for x in actions[0]:
for y in actions[1]:
self.transitions[(x, y)] = TransitionNode(self.players)
self.visited += 1
return score, depth, False
else:
if not self.transitions: #terminal node
score, log = s.rollout()
return score, depth, True
else: #recursive call
actionTuple = self.selectActions(verbose, mode)
transition = self.transitions[actionTuple]
if not transition.step:
transition.expand(s.apply(actionTuple, battle.Path()))
steps = transition.select()
path, s_ = s.apply(actionTuple, battle.Path(steps))[0]
child = transition.decisions[steps]
score, d, terminal = child.search(s_, depth+1, verbose, mode)
for _, a, n, v, in zip(score, actionTuple, self.N, self.V):
n[a] += 1
v[a] += _
self.visited += 1
return score, d, terminal
def selectActions(self, verbose=False, mode=None):
actionTuple = ()
data = [[], []]
for _ in self.players:
actions = self.N[_].keys()
for a in actions:
n = self.N[_][a] + 1
v = self.V[_][a]
if mode is None:
score = v/n + self.c * math.sqrt(math.log(self.visited)/n)
data[_].append((a, score))
actionTuple += (max(data[_],key= lambda datum : datum[1])[0],)
return actionTuple
class TransitionNode():
def __init__(self, players):
self.players = players
self.step = []
self.probs = []
self.paths = []
self.decisions = dict()
def expand(self, batch):
for path, state in batch:
self.step.append(path.step)
self.probs.append(path.p)
self.paths.append(path)
self.decisions[path.step] = DecisionNode(self.players)
def select(self):
steps = numpy.random.choice(self.paths, 1, p=self.probs)[0].step
return steps
Essentially, there are two different nodes, one for the player's decisions and one for chance. Crucially, all the methods for the search are in the class. It's a recursive search; at each decision node each player selects a move according to the UCT formula, that node has a dictionary of action tuples and points to a chance node. That chance node has a dict of decision nodes it points to, and it randomly samples a decision node. This continues deeper down the tree, until it hits a terminal node or an unexpanded decision node, where it expands and returns the rollout value back up the tree to the root.
Now this code is python slow but not slow. I've been timing how long it takes to do bathes of 3000 root to leaf traversals like above, and importantly it does not take any longer to do the tenth batch vs the first. You might expect it would take longer because of expansion of the tree, therefore deeper traversals. But in the case I gave it most of the traversals are hitting terminal nodes about 8 recursive calls in.
So why fix what's not broken? Well I noticed that my memory usage was exploding, probably because each node had its own copy of the methods attached. So, I rewrote the code so that both node classes only had statistics attached, and all the motion was provided by another Search class which contains all the methods. I also changed the provably ineffective UCT algorithm to a regret matching one. This is going to be slower, since its a bit more involved:
import numpy
import battle
import time
class Decision():
def __init__(self):
self.expanded = False
class Chance():
def __init__(self):
self.X = (0,0)
self.n = 0
class Search():
def __init__(self):
pass
def expandDecision(self, node, state):
node.actions = state.actions()
node.regrets = tuple([0 for a in _] for _ in node.actions)
node.strategies = tuple([0 for a in _] for _ in node.actions)
node.chances = dict()
for _ in range(len(node.actions[0])):
for __ in range(len(node.actions[1])):
node.chances[(_, __)] = Chance()
node.expanded = True
def expandChance(self, chance, state, moves):
chance.decisions = dict()
batch = state.apply(moves)
chance.steps = tuple(pair[0].step for pair in batch)
chance.p = tuple(pair[0].p for pair in batch)
def sampleChance(self, chance):
step = chance.steps[numpy.random.choice(range(len(chance.steps)), 1, p=chance.p)[0]]
if not step in chance.decisions.keys():
chance.decisions[step] = Decision()
return chance.decisions[step]
def getStrategy(self, node):
epsilon = 10**-3
s = [[max(__, epsilon) for __ in _] for _ in node.regrets]
t = [sum(_) for _ in s]
s = [[___/__ for ___ in _] for _, __ in zip(s, t)]
return s
def avgScore(self, chance):
return tuple((chance.X[_])/(chance.n + (chance.n == 0)) for _ in range(2))
def getRegrets(self, node, actions, u):
r = [[self.avgScore(node.chances[actions[:_] + (i,) + actions[_+1:]])[_] - u[_] for i in range(len(node.actions[_]))] for _ in range(2)]
r[0][actions[0]] = 0
r[1][actions[1]] = 0
return r
def sampleActions(self, strategy, gamma=0):
uniform = tuple([1/len(_)]*len(_) for _ in strategy)
fuzzy = tuple([(1-gamma)*x + (gamma)*y for x, y in zip(_, __)] for _, __ in zip(strategy, uniform))
return tuple(numpy.random.choice(range(len(_)), 1, p=_)[0] for _ in fuzzy)
def getMoves(self, node, actions):
return tuple(_[i] for i, _ in zip(actions, node.actions))
def updateDecision(self, node, r, s):
for _ in range(2):
for __ in range(len(node.actions[_])):
node.regrets[_][__] += r[_][__]
node.strategies[_][__] += s[_][__]
def updateChance(self, chance, u):
chance.X = tuple(chance.X[_] + u[_] for _ in range(2))
chance.n += 1
def averageStrategy(self, node):
return tuple(tuple(__/s for __ in _) for _, s in zip(node.strategies, map(sum, node.strategies)))
def search(self, node, state, gamma=0, depth=0):
if not node.expanded:
self.expandDecision(node, state)
return state.rollout()[0], depth, False
if not any(node.actions):
return state.rollout()[0], depth, True
sigma = self.getStrategy(node)
actions = self.sampleActions(sigma, gamma)
moves = self.getMoves(node, actions)
chance = node.chances[actions]
if not chance.n:
self.expandChance(chance, state, moves)
node_ = self.sampleChance(chance)
u, _, terminal = self.search(node_, state.apply(moves)[0][1], gamma, depth+1)
regrets = self.getRegrets(node, actions, u)
self.updateDecision(node, regrets, sigma)
self.updateChance(chance, u)
return self.avgScore(chance), _, terminal
if __name__ == '__main__':
node = Decision()
state = battle.s
__ = 3000
test = Search()
a = time.time()
total_depth = 0
terminal_count = 0
for _ in range(__ * 10):
test = Search()
u, depth, terminal = test.search(node, state, gamma=.03)
total_depth += depth
terminal_count += terminal
if (_+1)%__ == 0:
print(_+1)
for action, chance in node.chances.items():
print(node.actions)
print(action, chance.n, test.avgScore(chance))
print(test.averageStrategy(node))
#depth
avg_depth = total_depth/__
terminal_ratio = terminal_count/__
print('avg_depth: ', avg_depth)
print('terminal: ', terminal_ratio)
total_depth = 0
terminal_count = 0
#time
b = time.time()
print('t= ', b - a)
a = b
However this implementation is much slower. rather that a constant time (~20 sec) per batch, I have times of 20, 60, 100, 140 etc. Each batch of 3000 simulations is taking 40 seconds slower than the last!
I actually wrote a control case that uses the detached search of the later code and the UCT of the former and this still exhibits the slowdown effect. I thought maybe it was because I was reusing the same search object for all calls so I tried periodically re-instantiating it but to no avail.

Related

How to get python variable within class and method

I am currently creating my genetic algorithm and want to print the number of generations at the very end of the program when it finishes. However I am unsure how to access the counter variable that is the number of generations when it is outside of the class and method. So for example, at the end it would be like
Generation 100, average fit 18966, best fit 18947
Your best chromosone at generation 100
'\x06pzÂ\x8cYÆr¯n0q\x07l¿M8\x93Þ\x19\x87"\x01\x85\x1er\x89[F_VyER\x9b\x0bm=)\x9a\x9a¿¥\x10F\x12A\x84\x0fZ^\x14\x99\x8a4®\x9f¿*\\\xa0yi\x19E\x8aÇ+6(_<¾£cO~\x9c\x99\x932\x06\x0f\x82\x7f¤\x808xǸñA\x13\x0e<%\x06ÿ#í\x91Pô\x98 ®\r\x1b}\x89y¦\x0cqAK\tp\x95\x99ÔNj=Wn\x16\x94\x0cu!¯ñ\x13Qü[e8_ÂóU\x10\x1av_+%Q_¡ù\x87=\x08~ciÎ_Ï[\x8f#AëT\x14©qG\x89#Z«L\x9b¢\x94WL\x1dV¶R03\x84æ^ßr\x1fÃÈ\x1d\x8e Læª&®x\x94?TAÒD\x14£i\x82J\x15=w~\x03\x0c\xa0¾5\x02f5T\x91ol¢bIÞfk¬¡27W16(}6\x92\x87\n®xm0\x1a\n<8(à}ñ\x88̾\x17g\x9bj6\x8fI&\x12\x9aÂ\x9a_F\x1a\r[\x1dK\x15<.±DjcIy`98d>\x197Z\x91£%tIJ\x820\x93|\x07\x8dnÚ QÂ!Pf\x1d\nåòf\x91\x1d#S¾|\x9ff[d>O=T$ݶI\x9e»QÛÂ\x1d"¿U=û´F÷\x83C}wA\xa0É\x8aD\x93x»\x85\x7f\x14^\x0eL'
done:
100 generations
How do I exactly access the 100 from the method in the class?
import random
class GeneticAlgorithm(object):
def __init__(self, genetics):
self.genetics = genetics
pass
def run(self):
population = self.genetics.initial()
while True:
fits_pops = [(self.genetics.fitness(ch), ch) for ch in population]
if self.genetics.check_stop(fits_pops): break
population = self.next(fits_pops)
pass
return population
def next(self, fits):
parents_generator = self.genetics.parents(fits)
size = len(fits)
nexts = []
while len(nexts) < size:
parents = next(parents_generator)
cross = random.random() < self.genetics.probability_crossover()
children = self.genetics.crossover(parents) if cross else parents
for ch in children:
mutate = random.random() < self.genetics.probability_mutation()
nexts.append(self.genetics.mutation(ch) if mutate else ch)
pass
pass
return nexts[0:size]
pass
class GeneticFunctions(object):
def probability_crossover(self):
r"""returns rate of occur crossover(0.0-1.0)"""
return 1.0
def probability_mutation(self):
r"""returns rate of occur mutation(0.0-1.0)"""
return 0.0
def initial(self):
r"""returns list of initial population
"""
return []
def fitness(self, chromosome):
r"""returns domain fitness value of chromosome
"""
return len(chromosome)
def check_stop(self, fits_populations):
r"""stop run if returns True
- fits_populations: list of (fitness_value, chromosome)
"""
return False
def parents(self, fits_populations):
r"""generator of selected parents
"""
gen = iter(sorted(fits_populations))
while True:
f1, ch1 = next(gen)
f2, ch2 = next(gen)
yield (ch1, ch2)
pass
return
def crossover(self, parents):
r"""breed children
"""
return parents
def mutation(self, chromosome):
r"""mutate chromosome
"""
return chromosome
pass
if __name__ == "__main__":
"""
example: Mapped guess prepared Text
"""
class GuessText(GeneticFunctions):
def __init__(self, target_text,
limit=100, size=100,
prob_crossover=0.9, prob_mutation=0.2):
self.target = self.text2chromo(target_text)
self.counter = 0
self.limit = limit
self.size = size
self.prob_crossover = prob_crossover
self.prob_mutation = prob_mutation
pass
# GeneticFunctions interface impls
def probability_crossover(self):
return self.prob_crossover
def probability_mutation(self):
return self.prob_mutation
def initial(self):
return [self.random_chromo() for j in range(self.size)]
def fitness(self, chromo):
# larger is better, matched == 0
return -sum(abs(c - t) for c, t in zip(chromo, self.target))
def check_stop(self, fits_populations):
self.counter += 1
if self.counter % 100 == 0:
best_match = list(sorted(fits_populations))[-1][1]
fits = [f for f, ch in fits_populations]
best = -(max(fits))
ave = -(sum(fits) / len(fits))
print(
"Generation %3d, average fit %4d, best fit %4d" %
(self.counter, ave, best,
))
print("Your best chromosone at generation %3d" % self.counter)
print("%r" % self.chromo2text(best_match))
pass
return self.counter >= self.limit
def parents(self, fits_populations):
while True:
father = self.tournament(fits_populations)
mother = self.tournament(fits_populations)
yield (father, mother)
pass
pass
def crossover(self, parents):
father, mother = parents
index1 = random.randint(1, len(self.target) - 2)
index2 = random.randint(1, len(self.target) - 2)
if index1 > index2: index1, index2 = index2, index1
child1 = father[:index1] + mother[index1:index2] + father[index2:]
child2 = mother[:index1] + father[index1:index2] + mother[index2:]
return (child1, child2)
def mutation(self, chromosome):
index = random.randint(0, len(self.target) - 1)
vary = random.randint(-5, 5)
mutated = list(chromosome)
mutated[index] += vary
return mutated
# internals
def tournament(self, fits_populations):
alicef, alice = self.select_random(fits_populations)
bobf, bob = self.select_random(fits_populations)
return alice if alicef > bobf else bob
def select_random(self, fits_populations):
return fits_populations[random.randint(0, len(fits_populations)-1)]
def text2chromo(self, text):
return [ord(ch) for ch in text]
def chromo2text(self, chromo):
return "".join(chr(max(1, min(ch, 255))) for ch in chromo)
def random_chromo(self):
return [random.randint(1, 255) for i in range(len(self.target))]
pass
GeneticAlgorithm(GuessText("""The smartest and fastest Pixel yet.
Google Tensor: Our first custom-built processor.
The first processor designed by Google and made for Pixel, Tensor makes the new Pixel phones our most powerful yet.
The most advanced Pixel Camera ever.
Capture brilliant color and vivid detail with Pixels best-in-class computational photography and new pro-level lenses.""")).run()
print('done:')
print("%3d " 'generations' % counter)
pass
Define the GuessText first. Then access the counter.
gt = GuessText("""The smartest and fastest Pixel yet.
Google Tensor: Our first custom-built processor.
The first processor designed by Google and made for Pixel, Tensor makes the new Pixel phones our most powerful yet.
The most advanced Pixel Camera ever.
Capture brilliant color and vivid detail with Pixels best-in-class computational photography and new pro-level lenses.""")
GeneticAlgorithm(gt).run()
print('done:')
print("%3d " 'generations' % gt.counter)

A* algorithm TypeError: cannot unpack non-iterable int object

This is the python code which uses A* algorithm for finding solution for 8 puzzle problems, I got some error messages, how can I fix it?(The error message is under the code)
There are several object-oriented programming concepts for Problems class, Node class that are implemented to express the problem solution search that you need to understand in order to make the Python program complete. The priority queue is to make the nodes to be explored to be sorted according to their f-evaluation function score and return the min one as the first node to be searched next.
There is also a memorize function to memorize the heuristic value of state as a look-up table so that you don’t need to calculate the redundant computing of heuristic estimation value, so you can ignore it at this point if you don’t understand.
The components you need to implement is to make the abstract part of the program realizable for 8 -puzzle with the successor methods attached to a problem class which consists of initial state and goal state. Make sure the program can run correctly to generate the solution sequence that move the empty tile so that the 8-puzzle can move "Up", "Down", "Left", "Right", from initial state to goal state.
import math
infinity = math.inf
from itertools import chain
import numpy as np
import bisect
class memoize:
def __init__(self, f, memo={}):
self.f = f
self.memo = {}
def __call__(self, *args):
if not str(args) in self.memo:
self.memo[str(args)] = self.f(*args)
return self.memo[str(args)]
def coordinate(state):
index_state = {}
index = [[0,0], [0,1], [0,2], [1,0], [1,1], [1,2], [2,0], [2,1], [2,2]]
for i in range(len(state)):
index_state[state[i]] = index[i]
return index_state
def getInvCount(arr):
inv_count = 0
empty_value = -1
for i in range(0, 9):
for j in range(i + 1, 9):
if arr[j] != empty_value and arr[i] != empty_value and arr[i] > arr[j]:
inv_count += 1
return inv_count
def isSolvable(puzzle) :
inv_count = getInvCount([j for sub in puzzle for j in sub])
return (inv_count % 2 == 0)
def linear(state):
return sum([1 if state[i] != goal[i] else 0 for i in range(9)])
#memoize
def manhattan(state):
index_goal = coordinate(goal)
index_state = coordinate(state)
mhd = 0
for i in range(9):
for j in range(2):
mhd = abs(index_goal[i][j] - index_state[i][j]) + mhd
return mhd
#memoize
def sqrt_manhattan(state):
index_goal = coordinate(goal)
index_state = coordinate(state)
mhd = 0
for i in range(9):
for j in range(2):
mhd = (index_goal[i][j] - index_state[i][j])**2 + mhd
return math.sqrt(mhd)
#memoize
def max_heuristic(state):
score1 = manhattan(state)
score2 = linear(state)
return max(score1, score2)
class PriorityQueueElmt:
def __init__(self,val,e):
self.val = val
self.e = e
def __lt__(self,other):
return self.val < other.val
def value(self):
return self.val
def elem(self):
return self.e
class Queue:
def __init__(self):
pass
def extend(self, items):
for item in items: self.append(item)
class PriorityQueue(Queue):
def __init__(self, order=min, f=None):
self.A=[]
self.order=order
self.f=f
def append(self, item):
queueElmt = PriorityQueueElmt(self.f(item),item)
bisect.insort(self.A, queueElmt)
def __len__(self):
return len(self.A)
def pop(self):
if self.order == min:
return self.A.pop(0).elem()
else:
return self.A.pop().elem()
# Heuristics for 8 Puzzle Problem
class Problem:
def __init__(self, initial, goal=None):
self.initial = initial; self.goal = goal
def successor(self, state):
reachable = []
def get_key(val):
for key, value in index_state.items():
if val == value:
return key
return -1
def candidate(state, Position):
state = state.copy()
zero_index = state.index(0)
swap_index = state.index(get_key(Position))
state[zero_index], state[swap_index] = state[swap_index], state[zero_index]
return state
index_state = coordinate(state)
zero_position = index_state[0]
move_pair = {"left":[zero_position[0], zero_position[1] - 1],
"right":[zero_position[0], zero_position[1] + 1],
"up":[zero_position[0] - 1, zero_position[1]],
"down":[zero_position[0] + 1, zero_position[1]]
}
for action, position in move_pair.items():
#print(action, position)
if get_key(position) != -1:
reachable.append((action, candidate(state, position)))
#print(reachable)
return reachable
def goal_test(self, state):
return state == self.goal
def path_cost(self, c, state1, action, state2):
return c + 1
def value(self):
abstract
class Node:
def __init__(self, state, parent=None, action=None, path_cost=0, depth =0):
self.parent = parent
if parent:
self.depth = parent.depth + 1
else:
self.depth = 0
self.path_cost = path_cost
self.state = state
if action:
self.action = action
else: self.action = "init"
def __repr__(self):
return "Node state:\n " + str(np.array(self.state).reshape(3,3)) +"\n -> action: " + self.action + "\n -> depth: " + str(self.depth)
def path(self):
x, result = self, [self]
while x.parent:
result.append(x.parent)
x = x.parent
return result
def expand(self, problem):
for (act,n) in problem.successor(self.state):
if n not in [node.state for node in self.path()]:
yield Node(n, self, act,
problem.path_cost(self.path_cost, self.state, act, n))
def graph_search(problem, fringe):
closed = {}
fringe.append(Node(problem.initial,depth=0))
while fringe:
node = fringe.pop()
if problem.goal_test(node.state):
return node
if str(node.state) not in closed:
closed[str(node.state)] = True
fringe.extend(node.expand(problem))
return None
def best_first_graph_search(problem, f):
return graph_search(problem, PriorityQueue(min, f))
def astar_search(problem, h = None):
h = h or problem.h
def f(n):
return max(getattr(n, 'f', -infinity), n.path_cost + h(n.state))
return best_first_graph_search(problem, f)
def print_path(path, method):
print("*" * 30)
print("\nPath: (%s distance)" % method)
for i in range(len(path)-1, -1, -1):
print("-" * 15)
print(path[i])
goal = [1, 2, 3, 4, 5, 6, 7, 8, 0]
# Solving the puzzle
puzzle = [7, 2, 4, 5, 0, 6, 8, 3, 1]
if(isSolvable(np.array(puzzle).reshape(3,3))): # even true
# checks whether the initialized configuration is solvable or not
print("Solvable!")
problem = Problem(puzzle,goal)
path = astar_search(problem, manhattan).path()
print_path(path, "manhattan")
path = astar_search(problem, linear).path()
print_path(path, "linear")
path = astar_search(problem, sqrt_manhattan).path()
print_path(path, "sqrt_manhattan")
path = astar_search(problem, max_heuristic).path()
print_path(path, "max_heuristic")
else :
print("Not Solvable!") # non-even false
TypeError Traceback (most recent call last)
<ipython-input-124-2a60ddc8c009> in <module>
9 problem = Problem(puzzle,goal)
10
---> 11 path = astar_search(problem, manhattan).path()
12 print_path(path, "manhattan")
13
<ipython-input-123-caa97275712e> in astar_search(problem, h)
18 def f(n):
19 return max(getattr(n, 'f', -infinity), n.path_cost + h(n.state))
---> 20 return best_first_graph_search(problem, f)
21
22 def print_path(path, method):
<ipython-input-123-caa97275712e> in best_first_graph_search(problem, f)
12
13 def best_first_graph_search(problem, f):
---> 14 return graph_search(problem, PriorityQueue(min, f))
15
16 def astar_search(problem, h = None):
<ipython-input-123-caa97275712e> in graph_search(problem, fringe)
8 if str(node.state) not in closed:
9 closed[str(node.state)] = True
---> 10 fringe.extend(node.expand(problem))
11 return None
12
<ipython-input-121-e5a968bd54f0> in extend(self, items)
18
19 def extend(self, items):
---> 20 for item in items: self.append(item)
21
22 class PriorityQueue(Queue):
<ipython-input-122-db21613469b9> in expand(self, problem)
69
70 def expand(self, problem):
---> 71 for (act,n) in problem.successor(self.state):
72 if n not in [node.state for node in self.path()]:
73 yield Node(n, self, act,
TypeError: cannot unpack non-iterable int object
I got some error messages, how can I fix it?
There is one error message, The pieces of codes you get in the error message are the stack trace, which might help you to know how the execution got at the final point where the error occurred. In this case that is not so important. The essence of the error is this:
for (act,n) in problem.successor(self.state)
TypeError: cannot unpack non-iterable int object
So this means that the successor method returned an int instead of a list.
Looking at the code for successor, I notice that it intends to return a list called reachable, but there is a return statement right in the middle of the code, leaving the largest part of that code unexecuted (so-called "dead code"):
return state
This statement makes no sense where it is positioned. It seems to be an indentation problem: that return belongs inside the function just above it, like this:
def candidate(state, Position):
state = state.copy()
zero_index = state.index(0)
swap_index = state.index(get_key(Position))
state[zero_index], state[swap_index] = state[swap_index], state[zero_index]
return state # <-- indentation!

creating children on Genetic Algorithm

I am writing Python code for the implementation of the Genetic Algorithm.
I am stuck on creating children. I need this for my research. I have implemented the code to the best of my ability.
def initialise_city(num_dim, limit = 100):
X = np.random.randint(0,limit,size=num_dim)
return X
def initialise_cities(num_cities):
cities = []
for i in range(num_cities):
cities.append(initialise_city(2))
return cities
num_cities = 5
cities = initialise_cities(num_cities)
print("City Positions: ", cities)
def distance_function(cities, visit_order):
distance = 0.0
visit_pos = 0
next_pos = 0
for i, txt in enumerate(visit_order):
if (i < len(visit_order)-1):
visit_pos = visit_order[i]
next_pos = visit_order[i+1]
distance = distance + np.sqrt((cities[visit_pos][0]-cities[next_pos][0])**2 +(cities[visit_pos][1]-cities[next_pos][1])**2)
#raise NotImplementedError()
return -1.0*distance
def initialise_chromosome(chromosome_size):
# YOUR CODE HERE
chromosome = np.random.permutation(chromosome_size)
#raise NotImplementedError()
return chromosome
def initialise_population(population_size, chromosome_size):
population = []
# YOUR CODE HERE
for i in range(population_size):
population.append(initialise_chromosome(chromosome_size))
#raise NotImplementedError()
return population
def calculate_fitness(population, cities, fitness_function):
fitness_list = []
# YOUR CODE HERE
d = 0.0
for i,ix in enumerate(population):
d = fitness_function(cities,ix)
fitness_list.append(fitness_function(cities,ix))
#raise NotImplementedError()
return fitness_list
def selection(population, fitness_list):
## Select the top half of the best of the population
population = np.array(population)
sorted_indices = np.argsort(fitness_list)
selection_point = int(1+ len(fitness_list)/2)
# Randomply permute this top half of the poulation
indices = np.random.choice(sorted_indices[:selection_point], len(population))
best_population = population[indices]
return best_population
def pairing(selected_population):
## pair up parents that will be used to reproduce
count = 0
pairs = []
while count < len(selected_population)-1:
index = count
pairs.append([selected_population[index],selected_population[index+1]])
count +=2
return pairs
I am stuck on this part where children are supposed to be created.
def create_child(a,b):
child = []
# YOUR CODE HERE
point = random.randint(1,len(pairs))
#raise NotImplementedError()
return child
def cross_over(pairs):
final_population = []
for a,b in pairs:
child = create_child(a,b)
final_population.append(child)
child = create_child(b,a)
final_population.append(child)
return final_population

15 puzzle astar search that goes into an infinite loop

I am trying to develop a 15 star puzzle program in Python and its supposed to sort everything in numerical order using the a star search algorithm with the 0 being at the end.
Here is my a star algorithm I've developed so far:
"""Search the nodes with the lowest f scores first.
You specify the function f(node) that you want to minimize; for example,
if f is a heuristic estimate to the goal, then we have greedy best
first search; if f is node.depth then we have breadth-first search.
There is a subtlety: the line "f = memoize(f, 'f')" means that the f
values will be cached on the nodes as they are computed. So after doing
a best first search you can examine the f values of the path returned."""
def best_first_graph_search_manhattan(root_node):
start_time = time.time()
f = manhattan(root_node)
node = root_node
frontier = []
# how do we create this association?
heapq.heappush(frontier, node)
explored = set()
z = 0
while len(frontier) > 0:
node = heapq.heappop(frontier)
print(node.state.tiles)
explored.add(node)
if (goal_test(node.state.tiles)):
#print('In if statement')
path = find_path(node)
end_time = time.time()
z = z + f
return path, len(explored), z, (end_time - start_time)
for child in get_children(node):
# calcuate total cost
f_0 = manhattan(child)
z = z + f_0
print(z)
if child not in explored and child not in frontier:
#print('Pushing frontier and child')
heapq.heappush(frontier, child)
print('end of for loop')
return None
"""
Return the heuristic value for a given state using manhattan function
"""
def manhattan(node):
# Manhattan Heuristic Function
# x1, y1 = node.state.get_location()
# x2, y2 = self.goal
zero_location = node.state.tiles.index('0')
x1 = math.floor(zero_location / 4)
y1 = zero_location % 4
x2 = 3
y2 = 3
return abs(x2 - x1) + abs(y2 - y1)
"""
astar_search() is a best-first graph searching algortithim using equation f(n) = g(n) + h(n)
h is specified as...
"""
def astar_search_manhattan(root_node):
"""A* search is best-first graph search with f(n) = g(n)+h(n).
You need to specify the h function when you call astar_search, or
else in your Problem subclass."""
return best_first_graph_search_manhattan(root_node)
Here is the rest of my program. Assume that everything is working correctly in the following:
import random
import math
import time
import psutil
import heapq
#import utils.py
import os
import sys
from collections import deque
# This class defines the state of the problem in terms of board configuration
class Board:
def __init__(self,tiles):
self.size = int(math.sqrt(len(tiles))) # defining length/width of the board
self.tiles = tiles
#This function returns the resulting state from taking particular action from current state
def execute_action(self,action):
new_tiles = self.tiles[:]
empty_index = new_tiles.index('0')
if action=='l':
if empty_index%self.size>0:
new_tiles[empty_index-1],new_tiles[empty_index] = new_tiles[empty_index],new_tiles[empty_index-1]
if action=='r':
if empty_index%self.size<(self.size-1):
new_tiles[empty_index+1],new_tiles[empty_index] = new_tiles[empty_index],new_tiles[empty_index+1]
if action=='u':
if empty_index-self.size>=0:
new_tiles[empty_index-self.size],new_tiles[empty_index] = new_tiles[empty_index],new_tiles[empty_index-self.size]
if action=='d':
if empty_index+self.size < self.size*self.size:
new_tiles[empty_index+self.size],new_tiles[empty_index] = new_tiles[empty_index],new_tiles[empty_index+self.size]
return Board(new_tiles)
# This class defines the node on the search tree, consisting of state, parent and previous action
class Node:
def __init__(self,state,parent,action):
self.state = state
self.parent = parent
self.action = action
#self.initial = initial
#Returns string representation of the state
def __repr__(self):
return str(self.state.tiles)
#Comparing current node with other node. They are equal if states are equal
def __eq__(self,other):
return self.state.tiles == other.state.tiles
def __hash__(self):
return hash(self.state)
def __lt__(self, other):
return manhattan(self) < manhattan(other)
# Utility function to randomly generate 15-puzzle
def generate_puzzle(size):
numbers = list(range(size*size))
random.shuffle(numbers)
return Node(Board(numbers),None,None)
# This function returns the list of children obtained after simulating the actions on current node
def get_children(parent_node):
children = []
actions = ['l','r','u','d'] # left,right, up , down ; actions define direction of movement of empty tile
for action in actions:
child_state = parent_node.state.execute_action(action)
child_node = Node(child_state,parent_node,action)
children.append(child_node)
return children
# This function backtracks from current node to reach initial configuration. The list of actions would constitute a solution path
def find_path(node):
path = []
while(node.parent is not None):
path.append(node.action)
node = node.parent
path.reverse()
return path
# Main function accepting input from console , running iterative_deepening_search and showing output
def main():
global nodes_expanded
global path
global start_time
global cur_time
global end_time
nodes_expanded = 0
process = psutil.Process(os.getpid())
initial_memory = process.memory_info().rss / 1024.0
initial = str(input("initial configuration: "))
initial_list = initial.split(" ")
root = Node(Board(initial_list),None,None)
print(astar_search_manhattan(root))
final_memory = process.memory_info().rss / 1024.0
print('Directions: ', path)
print('Total Time: ', (end_time-start_time), ' seconds')
print('Total Memory: ',str(final_memory-initial_memory)+" KB")
print('Total Nodes Expanded: ', nodes_expanded)
# Utility function checking if current state is goal state or not
def goal_test(cur_tiles):
return cur_tiles == ['1','2','3','4','5','6','7','8','9','10','11','12','13','14','15','0']
if __name__=="__main__":main()
I've managed to narrow it down into my for loop in my best_first_graph_search_manhattan function and it appears that the infinite loop is caused if the if statement where its checking if child is not in explored and child is not in frontier. I'm unsure if its the way I'm calling my child function or the way I'm pushing frontier and child into my priority queue. I have imported heapq into my program and I've done extensive research where importing that function allows you to utilize priority queue into your program. Please don't mind other variables that are not used in my a star search.
Here is a test case: 1 0 3 4 5 2 6 8 9 10 7 11 13 14 15 12 | DRDRD
Thank you all very much for your help!

How to give start,end coordinates

I'm new to qgis and in here I want to find a path between two selected points on the map(Roads-vector layer). The points are selected by the user, using mouse clicks.
So here I used the astar algorithm to find path between two points.
*******************************astar.py**********************************
import heapq
class AStar(object):
def __init__(self, graphAstar):
self.graphAstar = graphAstar
def heuristic(self, node, start, end):
raise NotImplementedError
def search(self, start, end):
openset = set()
closedset = set()
current = start
openHeap = []
openset.add(current)
openHeap.append((0,current))
while openset:
temp = heapq.heappop(openHeap)
current = temp[1]
if current == end:
path = []
while current.parent:
path.append(current)
current = current.parent
path.append(current)
return path[::-1]
openset.remove(current)
closedset.add(current)
for node in self.graphAstar[current]:
if node in closedset:
continue
if node in openset:
new_g = current.gg + current.move_cost(node)
if node.gg > new_g:
node.gg = new_g
node.parent = current
else:
node.gg = current.gg + current.move_cost(node)
node.H = self.heuristic(node, start, end)
node.parent = current
openset.add(node)
heapq.heappush(openHeap, (node.H,node))
return None
class AStarNode(object):
def __init__(self):
self.gg = 0
self.H = 0
self.parent = None
def move_cost(self, other):
raise NotImplementedError
*****************************astar_grid.py*******************************
from astar import AStar, AStarNode
from math import sqrt
class AStarGrid(AStar):
def heuristic(self, node, start, end):
return sqrt((end.x - node.x)**2 + (end.y - node.y)**2)
class AStarGridNode(AStarNode):
def __init__(self, x, y):
self.x, self.y = x, y
super(AStarGridNode, self).__init__()
def move_cost(self, other):
diagonal = abs(self.x - other.x) == 1 and abs(self.y - other.y) == 1
return 14 if diagonal else 10
and in the main code, the following method is used to create graph from vector layer.
**************************plugin.py**********************************
def make_graph(self, mapinfo):
nodes = [[AStarGridNode(x, y) for y in range(mapinfo['height'])] for x in range(mapinfo['width'])]
graphAstar = {}
for x, y in product(range(mapinfo['width']), range(mapinfo['height'])):
node = nodes[x][y]
graphAstar[node] = []
for i, j in product([-1, 0, 1], [-1, 0, 1]):
if not (0 <= x + i < mapinfo['width']): continue
if not (0 <= y + j < mapinfo['height']): continue
graphAstar[nodes[x][y]].append(nodes[x+i][y+j])
return graphAstar, nodes
And I called that method in FindRoutes method..
def findRoutes(self):
vl=self.canvas.currentLayer()
director = QgsLineVectorLayerDirector( vl, -1, '', '', '', 3 )
properter = QgsDistanceArcProperter()
director.addProperter( properter )
crs = self.canvas.mapRenderer().destinationCrs()
builder = QgsGraphBuilder( crs )
global x1
global y1
global x2
global y2
pStart = QgsPoint( x1, y1 )
pStop = QgsPoint( x2, y2 )
graphAstar, nodes = self.make_graph({ "width": 8, "height": 8 })
paths = AStarGrid(graphAstar)
start, end = ??
path = paths.search(start, end)
My question is, how to pass the start and end coordinates to the function above? Because passing them just as coordinates (start, end = pStart, pStop) does not work.
How do add them to the graph created as nodes?
Or is there any easy way to do it?
Please help me to to find a solution to this problem.
Thank You
When i do an astar, the node i use are intern of the astar and contain a reference vers the original point object (your tuple of position).
Maybe it's the same with your AStarGridNode ?
In your case :
start = AStarGridNode(x1, y1)
stop = AStarGridNode(x2, y2)
This part could be in the your search function to hide this from the user.

Categories

Resources