Using uniform cost search on a matrix in python - python
Good day, I have an 11x11 matrix (shown below) where the 0s represent open spaces and the 1s represent walls. The horizontal and vertical movements are weighted at 1 and the diagonal movements are weighted at sqrt(2) The matrix looks as follows:
`board = [[0,0,0,0,0,0,0,0,0,0,0,0],
[0,0,0,0,0,1,1,1,0,1,1,0],
[0,1,0,0,0,0,1,1,0,1,1,0],
[0,1,1,0,0,0,0,0,0,1,1,0],
[0,1,1,1,0,0,0,0,0,1,1,0],
[0,1,1,1,1,0,0,0,0,1,1,0],
[0,1,1,1,1,1,1,1,1,1,1,0],
[1,1,1,1,1,1,1,1,1,1,1,0],
[0,0,0,0,0,0,0,0,0,0,1,0],
[0,0,0,0,0,0,0,0,0,0,1,0],
[0,0,1,1,1,1,1,1,1,1,1,0],
[0,0,0,0,0,0,0,0,0,0,0,0]]`
My goal is to write a Uniform cost search code in python to find the most cost effective path from a starting point (e.g [1,1]) to an end point (e.g [5,1]). Most of the code I have come across works with graphs and not matrices. I need help with working around this with a matrix.
I am fairly new at python and all help and advice will be highly appreciated. I am using python 3.
Since nobody seems to know an easy answer to this question I will post my (hopefully correct) answer. The used approach is not really efficient and based on a flood fill like algorithm.
First we define a list with all possible directions. Those are represented by a lambda function which return the new indices (xand y), the current weight and the current path:
from math import sqrt
dirs = [
lambda x, y, z, p: (x, y - 1, z + 1, p + [(x, y)]), # up
lambda x, y, z, p: (x, y + 1, z + 1, p + [(x, y)]), # down
lambda x, y, z, p: (x - 1, y, z + 1, p + [(x, y)]), # left
lambda x, y, z, p: (x + 1, y, z + 1, p + [(x, y)]), # right
lambda x, y, z, p: (x - 1, y - 1, z + sqrt(2), p + [(x, y)]), # up left
lambda x, y, z, p: (x + 1, y - 1, z + sqrt(2), p + [(x, y)]), # up right
lambda x, y, z, p: (x - 1, y + 1, z + sqrt(2), p + [(x, y)]), # down left
lambda x, y, z, p: (x + 1, y + 1, z + sqrt(2), p + [(x, y)]) # down right
]
Then we create some functions. The first one checks if the indices calculated by the directions are valid indices for the matrix and that there is no wall.
def valid(grid, x, y):
return 0 <= x < len(grid) and 0 <= y < len(grid[0]) and grid[x][y] == 0
The adjacent function yields every direction for every cell at the frontier (imagine it like a wave) and the flood function moves the wave one step forwards and replaces the old step with walls (1).
def adjacent(grid, frontier):
for (x, y, z, p) in frontier:
for d in dirs:
nx, ny, nz, np = d(x, y, z, p)
if valid(grid, nx, ny):
yield (nx, ny, nz, np)
def flood(grid, lst):
res = list(adjacent(grid, frontier))
for (x, y, z, p) in frontier:
grid[x][y] = 1
return res
In the following funtion we call the defined functions and return a tuple of the weight of the shortest path and the shortest path.
def shortest(grid, start, end):
start, end = tuple(start), tuple(end)
frontier = [(start[0], start[1], 0, [])]
res = []
while frontier and grid[end[0]][end[1]] == 0:
frontier = flood(grid, frontier)
for (x, y, z, p) in frontier:
if (x, y) == end:
res.append((z, p + [(x, y)]))
if not res:
return ()
return sorted(res)[0]
I tested it for (0, 0) to (8, 8) and the output seems plausable. It will probably fail if the cost for two horizontal / vertical steps is lower than the cost for the equal diagonal step.
EDIT: Result for (0, 0) to (8, 8) with P as path:
[[P,P,P,P,P,P,P,P,P,P,P,0],
[0,0,0,0,0,1,1,1,0,1,1,P],
[0,1,0,0,0,0,1,1,0,1,1,P],
[0,1,1,0,0,0,0,0,0,1,1,P],
[0,1,1,1,0,0,0,0,0,1,1,P],
[0,1,1,1,1,0,0,0,0,1,1,P],
[0,1,1,1,1,1,1,1,1,1,1,P],
[1,1,1,1,1,1,1,1,1,1,1,P],
[0,0,0,P,P,P,P,P,P,P,1,P],
[0,0,P,0,0,0,0,0,0,0,1,P],
[0,P,1,1,1,1,1,1,1,1,1,P],
[0,0,P,P,P,P,P,P,P,P,P,0]]
Weight: 39.071067811865476
EDIT 2: Add copy paste version.
from math import sqrt
board = [[0,0,0,0,0,0,0,0,0,0,0,0],
[0,0,0,0,0,1,1,1,0,1,1,0],
[0,1,0,0,0,0,1,1,0,1,1,0],
[0,1,1,0,0,0,0,0,0,1,1,0],
[0,1,1,1,0,0,0,0,0,1,1,0],
[0,1,1,1,1,0,0,0,0,1,1,0],
[0,1,1,1,1,1,1,1,1,1,1,0],
[1,1,1,1,1,1,1,1,1,1,1,0],
[0,0,0,0,0,0,0,0,0,0,1,0],
[0,0,0,0,0,0,0,0,0,0,1,0],
[0,0,1,1,1,1,1,1,1,1,1,0],
[0,0,0,0,0,0,0,0,0,0,0,0]]
dirs = [
lambda x, y, z, p: (x, y - 1, z + 1, p + [(x, y)]), # up
lambda x, y, z, p: (x, y + 1, z + 1, p + [(x, y)]), # down
lambda x, y, z, p: (x - 1, y, z + 1, p + [(x, y)]), # left
lambda x, y, z, p: (x + 1, y, z + 1, p + [(x, y)]), # right
lambda x, y, z, p: (x - 1, y - 1, z + sqrt(2), p + [(x, y)]), # up left
lambda x, y, z, p: (x + 1, y - 1, z + sqrt(2), p + [(x, y)]), # up right
lambda x, y, z, p: (x - 1, y + 1, z + sqrt(2), p + [(x, y)]), # down left
lambda x, y, z, p: (x + 1, y + 1, z + sqrt(2), p + [(x, y)]) # down right
]
def valid(grid, x, y):
return 0 <= x < len(grid) and 0 <= y < len(grid[0]) and grid[x][y] == 0
def adjacent(grid, frontier):
for (x, y, z, p) in frontier:
for d in dirs:
nx, ny, nz, np = d(x, y, z, p)
if valid(grid, nx, ny):
yield (nx, ny, nz, np)
def flood(grid, frontier):
res = list(adjacent(grid, frontier))
for (x, y, z, p) in frontier:
grid[x][y] = 1
return res
def shortest(grid, start, end):
start, end = tuple(start), tuple(end)
frontier = [(start[0], start[1], 0, [])]
res = []
while frontier and grid[end[0]][end[1]] == 0:
frontier = flood(grid, frontier)
for (x, y, z, p) in frontier:
if (x, y) == end:
res.append((z, p + [(x, y)]))
if not res:
return ()
return sorted(res)[0]
print(shortest(board, (0, 0), (8, 8)))
Related
How to use Sympy to automatically simplify expressions over a two-valued finite field?
I am trying to construct polynomials over a two-valued finite field {0, 1}, and I want them to automatically simplify using some identities that exist in this setting. I have tried the following: from sympy import * from sympy.polys.domains.finitefield import FiniteField x, y, z, t = symbols('x y z t') k = Poly(x+y * z*z + (x + y) + y + 1, domain=FiniteField(2)) This already simplifies to: Poly(y*z**2 + 1, x, y, z, modulus=2) However, the z**2 is actually the same as z in the field that I want to use. It does seem to automatically recognize that y + y = 0. How can I implement the other identity, z * z = z (idempotency)?
What you want doesn't seem to implemented for poly but maybe you can simulate the effect: In [54]: normalise = lambda p: Poly(ratsimpmodprime(p, [n**2-n for n in p.free_symbols]), modulus=2) In [55]: e = x+y * z*z + (x + y) + y + 1 In [56]: normalise(e) Out[56]: Poly(y*z + 1, x, y, z, modulus=2)
Fitting a polynomial function for a vector field in python
At first, thank you everybody for the amazing work on stackoverflow... you guys are amazing and have helped me out quite some times already. Regarding my problem: I have a series of vectors in the format (VectorX, VectorY, StartingpointX, StartingpointY) data = [(-0.15304757819399128, -0.034405679205349315, -5.42877197265625, 53.412933349609375), (-0.30532995491023485, -0.21523935094046465, -63.36669921875, 91.832427978515625), (-0.15872430479453215, -0.077999419482978283, -67.805389404296875, 81.001983642578125), (-0.36415549211687903, -0.33757147194808113, -59.015228271484375, 82.976226806640625), (0.0, 0.0, 0.0, 0.0), (-0.052973530805275004, 0.098212384392411423, 19.02667236328125, -13.72125244140625), (-0.34318724086483599, 0.17123742336019632, 80.0394287109375, 108.58499145507812), (0.19410169197834648, -0.17635303976555861, -55.603790283203125, -76.298828125), (-0.38774018337716143, -0.0824692384322816, -44.59942626953125, 68.402496337890625), (0.062202543524108478, -0.37219011831012949, -79.828826904296875, -10.764404296875), (-0.56582988168383963, 0.14872365390732512, 39.67657470703125, 97.303192138671875), (0.12496832467900276, -0.12216653754859408, 24.65948486328125, -30.92584228515625)] When I plot the vectorfield it looks like this: import numpy as np import matplotlib.pyplot as plt def main(): # Format Data... numdata = len(data) x = np.zeros(numdata) y = np.zeros(numdata) u = np.zeros(numdata) v = np.zeros(numdata) for i,el in enumerate(data): x[i] = el[2] y[i] = el[3] # length of vector z[i] = math.sqrt(el[0]**2+el[1]**2) u[i] = el[0] v[i] = el[1] # Plot plt.quiver(x,y,u,v ) # showing the length with color plt.scatter(x, y, c=z) plt.show() main() I want to create a polynomial function to fit a continous vector field for the whole area. After some research I found the following functions for fitting polynoms in two dimensions. The problem is, that it only accepts one value for the value that is fitted. def polyfit2d(x, y, z, order=3): ncols = (order + 1)**2 G = np.zeros((x.size, ncols)) ij = itertools.product(range(order+1), range(order+1)) for k, (i,j) in enumerate(ij): G[:,k] = x**i * y**j m, _, _, _ = np.linalg.lstsq(G, z) return m def polyval2d(x, y, m): order = int(np.sqrt(len(m))) - 1 ij = itertools.product(range(order+1), range(order+1)) z = np.zeros_like(x) for a, (i,j) in zip(m, ij): z += a * x**i * y**j return z Also when I tried to fit the one dimensional length of the vectors, the values returned from the polyval2d were completely off. Does anybody know a method to get a fitted function that will return a vector (x,y) for any point in the grid? Thank you!
A polynomial to fit a 2-d vector field will be two bivariate polynomials - one for the x-component and one for the y-component. In other words, your final polynomial fitting will look something like: P(x,y) = ( x + x*y, 1 + x + y ) So you will have to call polyfit2d twice. Here is an example: import numpy as np import itertools def polyfit2d(x, y, z, order=3): ncols = (order + 1)**2 G = np.zeros((x.size, ncols)) ij = itertools.product(range(order+1), range(order+1)) for k, (i,j) in enumerate(ij): G[:,k] = x**i * y**j m, _, _, _ = np.linalg.lstsq(G, z) return m def fmt1(x,i): if i == 0: return "" elif i == 1: return x else: return x + '^' + str(i) def fmt2(i,j): if i == 0: return fmt1('y',j) elif j == 0: return fmt1('x',i) else: return fmt1('x',i) + fmt1('y',j) def fmtpoly2(m, order): for (i,j), c in zip(itertools.product(range(order+1), range(order+1)), m): yield ("%f %s" % (c, fmt2(i,j))) xs = np.array([ 0, 1, 2, 3] ) ys = np.array([ 0, 1, 2, 3] ) zx = np.array([ 0, 2, 6, 12]) zy = np.array([ 1, 3, 5, 7]) mx = polyfit2d(xs, ys, zx, 2) print "x-component(x,y) = ", ' + '.join(fmtpoly2(mx,2)) my = polyfit2d(xs, ys, zy, 2) print "y-component(x,y) = ", ' + '.join(fmtpoly2(my,2)) In this example our vector field is: at (0,0): (0,1) at (1,1): (2,3) at (2,2): (6,5) at (3,3): (12,7) Also, I think I found a bug in polyval2d - this version gives more accurate results: def polyval2d(x, y, m): order = int(np.sqrt(len(m))) - 1 ij = itertools.product(range(order+1), range(order+1)) z = np.zeros_like(x) for a, (i,j) in zip(m, ij): z = z + a * x**i * y**j return z
Python checking if a point is in sphere with center x, y ,z
I'm trying to check if a point is within a sphere with a center point of (x, y, z) where (x, y, z) is not (0, 0, 0). This code I'm using to generate the points I want to check: def generatecoords(self, i): x, y, z = generatepoint() if i >= 1: valid = False while valid == False: coords = self.checkpoint(x, y, z) for b in world.starlist: if coords == world.starlist[b].coords: coords = self.checkpoint(x, y, z) else: valid = True else: coords = self.checkpoint(x, y, z) return coords def checkpoint(self, x, y, z): d = math.sqrt(x * x + y * y + z * z) while d >= self.radius: x, y, z = generatepoint() d = math.sqrt(x * x + y * y + z * z) coords = (int(x), int(y), int(z)) return coords def generatepoint(): x, y, z = [int(random.uniform(-self.radius, self.radius)) \ for b in range(3)] return x, y, z These function are called in a for loop to generate the points in a dictionary, while also checking the unlikely chance that points aren't placed on top of another(mostly because I can). I trying to figure out what I need to add to math.sqrt(x * x + y * y + z * z) so that it accounts for a center that isn't (0, 0, 0). I do know of one way to do it, but it would require several lines of code and I'd rather do it in one. I would have asked this in the comments of the answer in another question, but I'm not allowed to comment on answers yet.
The formula is: A point (x,y,z) is inside the sphere with center (cx,cy,cz) and radius r if (x - cx)^2 + (y - cy)^2 + (z - cz)^2 < r^2
Here is a very short function that returns True if the point is in the sphere, and False if not. The inputs are two numpy arrays: point = [x,y,z] and ref = [x,y,z] and the radius should be a float. import numpy as np def inSphere(self, point, ref, radius): # Calculate the difference between the reference and measuring point diff = np.subtract(point, ref) # Calculate square length of vector (distance between ref and point)^2 dist = np.sum(np.power(diff, 2)) # If dist is less than radius^2, return True, else return False return dist < radius ** 2
Python: how to calculate the bucket points
import numpy as np import math length = 10 points = [(1,2,3),(1,1,1),(23, 29, 0),(17, 0, 5)] bucketed_points = {} max_x = max([x for (x,y,z) in points]) max_y = max([y for (x,y,z) in points]) max_z = max([z for (x,y,z) in points]) x_buckets = int(max_x)/length + 1 y_buckets = int(max_y)/length + 1 z_buckets = int(max_z)/length + 1 for x in range(0, int(x_buckets)): for y in range(0, int(y_buckets)): for z in range(0, int(z_buckets)): bucketed_points["{0},{1},{2}".format(x, y, z)] = [] for point in points: # Here's where we actually put them in buckets x, y, z = point x, y, z = map(lambda a: int(math.floor(a/length)), [x, y, z]) bucketed_points["{0},{1},{2}".format(x, y, z)].append(point) print(bucketed_points) I have four points (1,2,3), (1,1,1),(23, 29, 0), (17, 0, 5), what I need to do is moving all the points to the new location (0,0,0), (0,0,0), (20,30,0), (20,0,10) which represent the center points for a cube whose side length equals 10 (length = 10).
X rotation doesn't work
In python, I have written a 3D rendering program. The Y rotation works fine, but the X rotation zooms in for some obscure reason. I couldn't spot it, so I put it up here. def plotLine(W, H, (x, y, z), (x2, y2, z2), rotX, rotY, FOV=1.0): try: x = float(x) y = float(y) z = float(z) x2 = float(x2) y2 = float(y2) z2 = float(z2) if z == 0: z = 0.01 if z2 == 0: z2 = 0.01 x, y, z = rotateY((x, y, z), rotY) x, y, z = rotateX((x, y, z), rotX) x2, y2, z2 = rotateY((x2, y2, z2), rotY) x2, y2, z2 = rotateX((x2, y2, z2), rotX) scX = (x/z)*FOV scY = (y/z)*FOV scX *= min(W, H) scY *= min(W, H) scX += W/2 scY += H/2 scX2 = (x2/z2)*FOV scY2 = (y2/z2)*FOV scX2 *= min(W, H) scY2 *= min(W, H) scX2 += W/2 scY2 += H/2 pygame.draw.aaline(display, (0, 255, 0), (scX, scY), (scX2, scY2)) except (OverflowError, ZeroDivisionError): return def rotateY((x, y, z), degrees): # Looking left and right. x, y, z = float(x), float(y), float(z) rads = math.radians(degrees) newX = (math.cos(rads)*x)+(math.sin(rads)*z) newY = y newZ = (-math.sin(rads)*x)+(math.cos(rads)*z) return (newX, newY, newZ) def rotateX((x, y, z), degrees): x, y, z = float(x), float(y), float(z) rads = math.radians(degrees) newX = x newY = (math.cos(rads)*y)+(math.sin(rads)*z) newZ = (math.sin(rads)*y)+(math.cos(rads)*z) return (newX, newY, newZ) Any help would be appreciated! BTW, I have looked up the matrix rotations on Wikipedia. Either Wikipedia got the matrices wrong, or I multiplied the matrices wrong, which is not likely. I have looked over them several times.
I think you have an error in your rotateX function newY = (math.cos(rads)*y)+(math.sin(rads)*z) newZ = (math.sin(rads)*y)+(math.cos(rads)*z) should be newY = (math.cos(rads)*y)+(math.sin(rads)*z) newZ = (-math.sin(rads)*y)+(math.cos(rads)*z) ^ ^ without the negative sign you will not get a rotation. You have done this correctly in your rotateY function but not the rotateX function. If you look at the 2D submatrix of your currently coded 3D rotation you have [cos(rads) sin(rads)] [sin(rads) cos(rads)] and the determinant of this is 1/(cos(rads)*cos(rads) - sin(rads)sin(rads)) = 1/cos(2*rads) This is not equal to 1 for all angles rads and hence is not a rotation for all values of rads. Note also that this rotation angle would be in the negative sense to what is usually associated with a rotation. You can see more information about this here on wikepedia