Run linear and bisect search in parallel and stop on first result - python

So I have been searching far an wide for this. I want to run three functions in parallel, when one finishes return this value, stop the two others.
I have tried with asynciop, multiprocess and concurrent.futures, and I still can not get it to work.
As an example I have a function that returns 0 when above a certain threshold, and some number otherwise. To test my implementation I want to return the last non-zero value.
My problem is that regardless of whatever I try, all three methods complete every time. Instead of breaking when a match has been found.
from concurrent.futures import FIRST_COMPLETED, ThreadPoolExecutor, wait
def first_zero(f, values=[10, 10**2, 10**3]):
for i in values:
if f(i) == 0:
return i
return values[-1]
def bisect_search(f, first, last):
if f(first) == 0:
return first
middle = (last + first) // 2
while True:
# print(middle, "bisect")
if abs(last - first) <= 1:
return first
middle = (last + first) // 2
if f(middle) > 0:
first = middle
else:
last = middle
def linear_search(f, start, stop, increment):
i = start
minimum = min(start, stop)
maximum = max(start, stop)
if increment < 0:
while minimum <= i <= maximum:
if f(i) != 0:
return i
i += increment
else:
while minimum <= i <= maximum:
if f(i) == 0:
return i - increment
i += increment
return i
def linear_binary_search(f, start=0):
stop = first_zero(f)
tasks = [
(linear_search, [f, start, stop, 1]),
(linear_search, [f, stop, start, -1]),
(bisect_search, [f, start, stop]),
]
executor = ThreadPoolExecutor(max_workers=len(tasks))
futures = [executor.submit(task, *args) for (task, args) in tasks]
done, _ = wait(futures, return_when=FIRST_COMPLETED)
executor.shutdown(wait=False, cancel_futures=True)
return done.pop().result()
if __name__ == "__main__":
import random
import time
target = 995
def f(x):
time.sleep(0.01)
if x <= target:
return random.randint(1, 1000)
return 0
# Comment the following line to see
# how much slower the concurrent version is
print(linear_binary_search(f))
stop = first_zero(f)
print(linear_search(f, stop, 0, -1))

Related

Python calls the same function in a loop, and uses the return value of the last call as the parameter of this call

This is kmeans I wrote. I want to call this function cyclically to repeatedly calculate the distance to the center point and enter the return as parameter
the first input is (b[0,:],b[1,:])
def DIV(point1,point2):
plt.figure()
c = []
d = []
for i in range(1000):
dist1 = np.linalg.norm(Z[i] - point1)
dist2 = np.linalg.norm(Z[i] - point2)
if dist1 > dist2:
c.append(Z[i])
else:
d.append(Z[i])
C = np.array(c)
D = np.array(d)
plt.scatter(C[:,0],C[:,1],s=16.,color='green')
plt.scatter(D[:,0],D[:,1],s=16.,color='yellow')
center1 = C.mean(axis=0)
center2 = D.mean(axis=0)
plt.scatter(center1[0],center1[1],marker ='x',s=16.,color='red')
plt.scatter(center2[0],center2[1],marker ='x',s=16.,color='red')
return center1,center2
Your function and background don't matter much, sounds like you are basically trying to do a "fixed point" function based on your response to my comment:
def fix(func, initial, max_iter):
prev = func(initial)
iteration = 1
while True:
curr = func(prev)
if curr == prev or iteration > max_iter:
break
prev = curr
iteration += 1
return curr
For your specific case, you could then write
div = lambda (a, b): DIV(a, b)
result = fix(div, (b[0,:],b[1,:]), max_iter=1000)
if you wanted to use the same logic inline, instead of using more generic logic, you could write
prev = DIV(b[0,:],b[1,:])
iteration = 1
while True:
curr = DIV(*prev)
if curr == prev or iteration > 1000:
break
prev = curr
iteration += 1
result = curr
I used very cumbersome loops and functions to control its iteration
def paste(head, start=0, stop=0, step=1, sep=''):
return [f'{head}{sep}{i}' for i in range(start, stop, step)]
def loop(n):
k = paste('a', 0, n)
k[0],k[1] = DIV(b[0,:],b[1,:])
for i in range(0,n,2):
k[i],k[i+1] = DIV(k[i],k[i+1])
for j in range(2,n,2):
k[j],k[j+1] = DIV(k[i],k[i+1])
loop(6)

How can I implement IDA* algorithm in Python for 15-Puzzle problem?

I'm trying to solve the 15-Puzzle problem using IDA* algorithm and Manhattan heuristic.
I already implemented the algorithm from the pseudocode in this Wikipedia page (link).
Here's my code so far :
def IDA(initial_state, goal_state):
initial_node = Node(initial_state)
goal_node = Node(goal_state)
threshold = manhattan_heuristic(initial_state, goal_state)
path = [initial_node]
while 1:
tmp = search(path, goal_state, 0, threshold)
if tmp == True:
return path, threshold
elif tmp == float('inf'):
return False
else:
threshold = tmp
def search(path, goal_state, g, threshold):
node = path[-1]
f = g + manhattan_heuristic(node.state, goal_state)
if f > threshold:
return f
if np.array_equal(node.state, goal_state):
return True
minimum = float('inf')
for n in node.nextnodes():
if n not in path:
path.append(n)
tmp = search(path, goal_state, g + 1, threshold)
if tmp == True:
return True
if tmp < minimum:
minimum = tmp
path.pop()
return minimum
def manhattan_heuristic(state1, state2):
size = range(1, len(state1) ** 2)
distances = [count_distance(num, state1, state2) for num in size]
return sum(distances)
def count_distance(number, state1, state2):
position1 = np.where(state1 == number)
position2 = np.where(state2 == number)
return manhattan_distance(position1, position2)
def manhattan_distance(a, b):
return abs(b[0] - a[0]) + abs(b[1] - a[1])
class Node():
def __init__(self, state):
self.state = state
def nextnodes(self):
zero = np.where(self.state == 0)
y,x = zero
y = int(y)
x = int(x)
up = (y - 1, x)
down = (y + 1, x)
right = (y, x + 1)
left = (y, x - 1)
arr = []
for direction in (up, down, right, left):
if len(self.state) - 1 >= direction[0] >= 0 and len(self.state) - 1 >= direction[1] >= 0:
tmp = np.copy(self.state)
tmp[direction[0], direction[1]], tmp[zero] = tmp[zero], tmp[direction[0], direction[1]]
arr.append(Node(tmp))
return arr
I'm testing this code with a 3x3 Puzzle and here's the infinite loop! Due to the recursion I have some trouble testing my code...
I think the error might be here : tmp = search(path, goal_state, g + 1, threshold) (in the search function). I'm adding only one to the g cost value. It should be correct though, because I can only move a tile 1 place away.
Here's how to call the IDA() function:
initial_state = np.array([8 7 3],[4 1 2],[0 5 6])
goal_state = np.array([1 2 3],[8 0 4],[7 6 5])
IDA(initial_state, goal_state)
Can someone help me on this ?
There are couple of issues in your IDA* implementation. First, what is the purpose of the variable path? I found two purposes of path in your code:
Use as a flag/map to keep the board-states that is already been visited.
Use as a stack to manage recursion states.
But, it is not possible to do both of them by using a single data structure. So, the first modification that your code requires:
Fix-1: Pass current node as a parameter to the search method.
Fix-2: flag should be a data structure that can perform a not in query efficiently.
Now, fix-1 is easy as we can just pass the current visiting node as the parameter in the search method. For fix-2, we need to change the type of flag from list to set as:
list's average case complexity for x in s is: O(n)
set's
Average case complexity for x in s is: O(1)
Worst case complexity for x in s is: O(n)
You can check more details about performance for testing memberships: list vs sets for more details.
Now, to keep the Node information into a set, you need to implement __eq__ and __hash__ in your Node class. In the following, I have attached the modified code.
import timeit
import numpy as np
def IDA(initial_state, goal_state):
initial_node = Node(initial_state)
goal_node = Node(goal_state)
threshold = manhattan_heuristic(initial_state, goal_state)
#print("heuristic threshold: {}".format(threshold))
loop_counter = 0
while 1:
path = set([initial_node])
tmp = search(initial_node, goal_state, 0, threshold, path)
#print("tmp: {}".format(tmp))
if tmp == True:
return True, threshold
elif tmp == float('inf'):
return False, float('inf')
else:
threshold = tmp
def search(node, goal_state, g, threshold, path):
#print("node-state: {}".format(node.state))
f = g + manhattan_heuristic(node.state, goal_state)
if f > threshold:
return f
if np.array_equal(node.state, goal_state):
return True
minimum = float('inf')
for n in node.nextnodes():
if n not in path:
path.add(n)
tmp = search(n, goal_state, g + 1, threshold, path)
if tmp == True:
return True
if tmp < minimum:
minimum = tmp
return minimum
def manhattan_heuristic(state1, state2):
size = range(1, len(state1) ** 2)
distances = [count_distance(num, state1, state2) for num in size]
return sum(distances)
def count_distance(number, state1, state2):
position1 = np.where(state1 == number)
position2 = np.where(state2 == number)
return manhattan_distance(position1, position2)
def manhattan_distance(a, b):
return abs(b[0] - a[0]) + abs(b[1] - a[1])
class Node():
def __init__(self, state):
self.state = state
def __repr__(self):
return np.array_str(self.state.flatten())
def __hash__(self):
return hash(self.__repr__())
def __eq__(self, other):
return self.__hash__() == other.__hash__()
def nextnodes(self):
zero = np.where(self.state == 0)
y,x = zero
y = int(y)
x = int(x)
up = (y - 1, x)
down = (y + 1, x)
right = (y, x + 1)
left = (y, x - 1)
arr = []
for direction in (up, down, right, left):
if len(self.state) - 1 >= direction[0] >= 0 and len(self.state) - 1 >= direction[1] >= 0:
tmp = np.copy(self.state)
tmp[direction[0], direction[1]], tmp[zero] = tmp[zero], tmp[direction[0], direction[1]]
arr.append(Node(tmp))
return arr
initial_state = np.array([[8, 7, 3],[4, 1, 2],[0, 5, 6]])
goal_state = np.array([[1, 2, 3],[8, 0, 4],[7, 6, 5]])
start = timeit.default_timer()
is_found, th = IDA(initial_state, goal_state)
stop = timeit.default_timer()
print('Time: {} seconds'.format(stop - start))
if is_found is True:
print("Solution found with heuristic-upperbound: {}".format(th))
else:
print("Solution not found!")
Node: Please double check your Node.nextnodes() and manhattan_heuristic() methods as I did not pay much attention in those areas. You can check this GitHub repository for other algorithmic implementations (i.e., A*, IDS, DLS) to solve this problem.
References:
Python Wiki: Time Complexity
TechnoBeans: Performance for testing memberships: list vs tuples vs sets
GitHub: Puzzle Solver (by using problem solving techniques)

heap time complexity python

import heapq
def getMaxUnit(num,boxes,UnitSize,UnitSize,unitPerBox, truckSize):
if truckSize == 0 or num == 0:
return 0
h = []
for i in range(num):
h.append((-1*unitPerBox[i],boxes[i]))
heapq.heapify(h)
maxCapacity = 0
while truckSize>=0 and len(h) != 0:
popped = heapq.heappop(h)
truckSize = truckSize-popped[1]
available = popped[1]
if truckSize < 0:
available = popped[1]+truckSize
maxCapacity = maxCapacity + available*(-1*popped[0])
return maxCapacity
I'm trying to find the time complexity of the code here. I'm confused with what the time complexity of heapq.heappop here as it needs to maintain heap property every time we pop an element.

Why is this list index out of range?

I am currently working on a school assignment from the AI department where I have to make a Genetic Algorithm. It is meant to solve the 8 Queens problem using a list called board, where each index is a column and the value in that index is the row. The algorithm itself works fine, but every time I try to run it, it crashes after the while loop reaches populationSize - 2 (in this case 18). The error that I get is about line:
child = reproduce(population[l], population[l + 1], nqueens)
and the error is:
Traceback (most recent call last):
File "nqueens1.py", line 370, in
main()
File "nqueens1.py", line 363, in main
genetic_algorithm(board)
File "nqueens1.py", line 291, in genetic_algorithm
child = reproduce(population[l], population[l + 1], nqueens)
IndexError: list index out of range
I am trying to understand what is going wrong, but I do not see why it would go out of range. Here is the code that I have so far:
Function Reproduce
def reproduce(boardX, boardY, nqueens):
boardChild = [-1] * nqueens
n = random.randint(0, (nqueens - 1)) #percentage of how much of one parent is reproduced and how much of the other parent
d = 0
for d in range(n): # the first n part of parent x
boardChild[d] = boardX[d]
s = d + 1
for s in range(nqueens) : # the last n-(len(board)-1) part of parent y
boardChild[s] = boardY[s]
return boardChild
Function Mutate
def mutate(child):
print('mutate')
boardMutated = [-1] * len(child)
newColumn = random.randint(0, len(child)-1)
newRow = random.randint(0, len(child)-1)
boardMutated[newColumn] = newRow
return boardMutated
Genetic Algorithm
def genetic_algorithm(board):
optimum = (len(board) - 1) * len(board) / 2
print('optimum:' + str(optimum))
nqueens = len(board)
populationSize = 20
population = []
for i in range(populationSize -1): #initializes first pooulation
population.append([])
for _ in range(nqueens):
population[i].append(random.randint(0, nqueens-1))
population[i].append(evaluate_state(population[i]))
if evaluate_state(population[i]) == optimum:
print("solved puzzle! 0")
print_board(population[i])
return
t = 0
while t != 1000:
population.sort(key=lambda x: x[nqueens -1 ]) #sorts the population from highest population size
for i in range(populationSize -1):
del population[i][-1]
newPopulation = [ [] for _ in range(populationSize -1) ]
newPopulation[0] = reproduce(population[1], population[0], nqueens) #
chance = random.randint(0, 100)
if chance < 5: # small chance that the child gets mutated
newPopulation[0] = mutate(newPopulation[0])
if evaluate_state(newPopulation[0]) == optimum:
print('if evaluate_state(child) == optimum:')
print("Solved puzzle! 1")
print_board(newPopulation[0])
return
if evaluate_state(newPopulation[0]) == optimum:
print("Solved puzzle! 2")
print('if evaluate_state(newPopulation[1]) == optimum:')
print_board(newPopulation[1])
return
l = 0
while l != (populationSize -1):
print(str(l))
child = reproduce(population[l], population[l + 1], nqueens) # reproduces the new generation
print_board(child)
if evaluate_state(child) == optimum:
print('if evaluate_state(child) == optimum:')
print("Solved puzzle! 3")
print_board(child)
return
chance = random.randint(0, 100)
if chance < 5: # small chance that the child gets mutated
child = mutate(child)
if evaluate_state(child) == optimum:
print('if evaluate_state(child) == optimum:')
print("Solved puzzle! 4")
print_board(child)
return
newPopulation[l] = child
l += 1
t += 1
All the print-statements were added to see which parts did execute and which did not execute. The code crashes as soon as the l reaches 18 here, which of course it should not. All help would be appreciated!
I think population only has 19 items, not 20; all the populations are initialized using range(populationSize - 1) which only has 19 numbers (from 0 to 18).
You can check this by also printing out len(population)

Python: N Puzzle (8-Puzzle) Solver Heruistics: How to iterate?

I have written a selection of functions to try and solve a N-puzzle / 8-puzzle.
I am quite content with my ability to manipulate the puzzle but am struggling with how to iterate and find the best path. My skills are not in OOP either and so the functions are simple.
The idea is obviously to reduce the heruistic distance and place all pieces in their desired locations.
I have read up a lot of other questions regarding this topic but they're often more advanced and OOP focused.
When I try and iterate through there are no good moves. I'm not sure how to perform the A* algorithm.
from math import sqrt, fabs
import copy as cp
# Trial puzzle
puzzle1 = [
[3,5,4],
[2,1,0],
[6,7,8]]
# This function is used minimise typing later
def starpiece(piece):
'''Checks the input of a *arg and returns either tuple'''
if piece == ():
return 0
elif isinstance(piece[0], (str, int)) == True:
return piece[0]
elif isinstance(piece[0], (tuple, list)) and len(piece[0]) == 2:
return piece[0]
# This function creates the goal puzzle layout
def goal(puzzle):
'''Input a nested list and output an goal list'''
n = len(puzzle) * len(puzzle)
goal = [x for x in range(1,n)]
goal.append(0)
nested_goal = [goal[i:i+len(puzzle)] for i in range(0, len(goal), len(puzzle))]
return nested_goal
# This fuction gives either the coordinates (as a tuple) of a piece in the puzzle
# or the piece in the puzzle at give coordinates
def search(puzzle, *piece):
'''Input a puzzle and piece value and output a tuple of coordinates.
If no piece is selected 0 is chosen by default. If coordinates are
entered the piece value at those coordinates are outputed'''
piece = starpiece(piece)
if isinstance(piece, (tuple, list)) == True:
return puzzle[piece[0]][piece[1]]
for slice1, sublist in enumerate(puzzle):
for slice2, item in enumerate(sublist):
if puzzle[slice1][slice2] == piece:
x, y = slice1, slice2
return (x, y)
# This function gives the neighbours of a piece at a given position as a list of coordinates
def neighbours(puzzle, *piece):
'''Input a position (as a tuple) or piece and output a list
of adjacent neighbours. Default are the neighbours to 0'''
length = len(puzzle) - 1
return_list = []
piece = starpiece(piece)
if isinstance(piece, tuple) != True:
piece = search(puzzle, piece)
if (piece[0] - 1) >= 0:
x_minus = (piece[0] - 1)
return_list.append((x_minus, piece[1]))
if (piece[0] + 1) <= length:
x_plus = (piece[0] + 1)
return_list.append((x_plus, piece[1]))
if (piece[1] - 1) >= 0:
y_minus = (piece[1] - 1)
return_list.append((piece[0], y_minus))
if (piece[1] + 1) <= length:
y_plus = (piece[1] + 1)
return_list.append((piece[0], y_plus))
return return_list
# This function swaps piece values of adjacent cells
def swap(puzzle, cell1, *cell2):
'''Moves two cells, if adjacent a swap occurs. Default value for cell2 is 0.
Input either a cell value or cell cooridinates'''
cell2 = starpiece(cell2)
if isinstance(cell1, (str, int)) == True:
cell1 = search(puzzle, cell1)
if isinstance(cell2, (str, int)) == True:
cell2 = search(puzzle, cell2)
puzzleSwap = cp.deepcopy(puzzle)
if cell1 == cell2:
print('Warning: no swap occured as both cell values were {}'.format(search(puzzle,cell1)))
return puzzleSwap
elif cell1 in neighbours(puzzleSwap, cell2):
puzzleSwap[cell1[0]][cell1[1]], puzzleSwap[cell2[0]][cell2[1]] = puzzleSwap[cell2[0]][cell2[1]], puzzleSwap[cell1[0]][cell1[1]]
return puzzleSwap
else:
print('''Warning: no swap occured as cells aren't adjacent''')
return puzzleSwap
# This function gives true if a piece is in it's correct position
def inplace(puzzle, p):
'''Ouputs bool on whether a piece is in it's correct position'''
if search(puzzle, p) == search(goal(puzzle), p):
return True
else:
return False
# These functions give heruistic measurements
def heruistic(puzzle):
'''All returns heruistic (misplaced, total distance) as a tuple. Other
choices are: heruistic misplaced, heruistic distance or heruistic list'''
heruistic_misplaced = 0
heruistic_distance = 0
heruistic_distance_total = 0
heruistic_list = []
for sublist in puzzle:
for item in sublist:
if inplace(puzzle, item) == False:
heruistic_misplaced += 1
for sublist in puzzle:
for item in sublist:
a = search(puzzle, item)
b = search(goal(puzzle), item)
heruistic_distance = int(fabs(a[0] - b[0]) + fabs(a[1] - b[1]))
heruistic_distance_total += heruistic_distance
heruistic_list.append(heruistic_distance)
return (heruistic_misplaced, heruistic_distance_total, heruistic_list)
def hm(puzzle):
'''Outputs heruistic misplaced'''
return heruistic(puzzle)[0]
def hd(puzzle):
'''Outputs total heruistic distance'''
return heruistic(puzzle)[1]
def hl(puzzle):
'''Outputs heruistic list'''
return heruistic(puzzle)[2]
def hp(puzzle, p):
'''Outputs heruistic distance at a given location'''
x, y = search(puzzle, p)[0], search(puzzle, p)[1]
return heruistic(puzzle)[2][(x * len(puzzle)) + y]
# This is supposted to iterate along a route according to heruistics but doesn't work
def iterMove(puzzle):
state = cp.deepcopy(puzzle)
while state != goal(puzzle):
state_hd = hd(state)
state_hm = hm(state)
moves = neighbours(state)
ok_moves = []
good_moves = []
for move in moves:
maybe_state = swap(state, move)
if hd(maybe_state) < state_hd and hm(maybe_state) < state_hm:
good_moves.append(move)
elif hd(maybe_state) < state_hd:
ok_moves.append(move)
elif hm(maybe_state) < state_hm:
ok_moves.append(move)
if good_moves != []:
print(state)
state = swap(state, good_moves[0])
elif ok_moves != []:
print(state)
state = swap(state, ok_moves[0])
>> iterMove(puzzle1)
'no good moves'
To implement A* in Python you can use https://docs.python.org/3/library/heapq.html for a priority queue. You put possible positions into the queue with a priority of "cost so far + heuristic for remaining cost". When you take them out of the queue you check a set of already seen positions. Skip this one if you've seen the position, else add it to the set and then process.
An untested version of the critical piece of code:
queue = [(heuristic(starting_position), 0, starting_position, None)]
while 0 < len(queue):
(est_moves, cur_moves, position, history) = heapq.heappop(queue)
if position in seen:
continue
elif position = solved:
return history
else:
seen.add(position)
for move in possible_moves(position):
next_position = position_after_move(position, move)
est_moves = cur_moves + 1 + heuristic(next_position)
heapq.heappush(queue,
(est_moves, cur_moves+1,
next_position, (move, history)))
return None

Categories

Resources