first version

This commit is contained in:
Johann Dreo 2020-06-25 10:21:19 +02:00
commit 624c92e934
10 changed files with 1333 additions and 0 deletions

206
python/code.py Normal file
View file

@ -0,0 +1,206 @@
import sys
import math
from queue import PriorityQueue
def x(p):
return p[0]
def y(p):
return p[1]
def distance(u,v):
return math.sqrt( (x(u)-x(v))*(x(u)-x(v)) + (y(u)-y(v))*(y(u)-y(v)) )
def tour(lst):
"""Yield a sequence of consecutive pairs of items across a given container."""
assert(len(lst)>=2)
# consecutive pairs in lst + last-to-first element
for a,b in zip(lst, lst[1:] + [lst[0]]):
yield (a,b)
# This is a very bad way to implement frange,
# you should use nympy.linspace in real code…
def frange(x, y, s):
while x < y:
yield x
x += s
def neighbors_grid(p, grid_step, pmin, pmax, directions):
"""
Compute coordinates of the neighbors of the given point, according to grid parameters and directions.
Directions are supposed to be given in a specific order
(in this project, the transit_in_simplex functions will necessitate clockwise order).
Args:
point: The considered point.
grid_step: Length of an orthogonal edge of the grid.
pmin: Corner point of the grid, with minimal coordinates.
pmax: Corner point of the grid, with maximal coordinates.
Returns:
A sequence of neighbors points.
"""
neighbors = []
for d in directions:
nx = x(p)+x(d)*grid_step
ny = y(p)+y(d)*grid_step
n = (nx, ny)
if x(pmin) <= x(n) <= x(pmax) and y(pmin) <= y(n) <= y(pmax):
neighbors.append(n)
assert(len(neighbors) >= 2)
return neighbors
def transit_on_edge(point, neighbors, costs):
"""
Find the transit of minimal cost among the given edges.
Edges are given as the considered point and the sequence of neighbors points.
"""
mincost = float("inf")
for n in neighbors:
# Do not compute transition toward points without a cost
# (i.e. Supposedly, only toward the front).
if n in costs:
# Cost of the transition from/to p from/to n.
c = costs[n] + distance(point,n)
if c < mincost:
mincost = c
# Should be near the front (and thus have found a transit).
assert(mincost != float("inf"))
return mincost
def transit_in_simplex(p, neighbors, costs, eps):
mincost = float("inf")
# Special case: having a single point with a cost in the neighborhood.
# e.g. the seed.
# This would make an empty tour and thus needs a special case.
with_costs = [n for n in neighbors if n in costs]
if len(with_costs) == 1:
# There is only one possible transition.
i = with_costs[0]
mincost = costs[i] + distance(p,i)
else:
for edge in tour(neighbors):
pj, pk = edge
# Do not compute transition toward points without a cost
# (i.e. only toward the front).
if pj in costs and pk in costs:
# Cost of the transition from/to p from/to edge e.
# This is the simplest way to minimize the transit, even if not the most efficient.
for z in frange(0,1,eps):
xj,yj = pj
xk,yk = pk
zx = z*xj + (1-z)*xk
zy = z*yj + (1-z)*yk
n = (zx,zy)
# Linear interpolation of costs.
c = z*costs[pj] + (1-z)*costs[pk] + distance(p,n)
if c < mincost:
mincost = c
# If the front is reached on a single point.
elif pj in costs and pk not in costs:
c = costs[pj] + distance(p,pj)
if c < mincost:
mincost = c
elif pj not in costs and pk in costs:
c = costs[pk] + distance(p,pk)
if c < mincost:
mincost = c
# Should be near the front (and thus have found a transit).
assert(mincost != float("inf"))
return mincost
def algo_run(seed, iterations, neighbors, transit_func):
"""
Propagate the front from the given seed, during the given number of iterations.
Iteratively accept points of minimal costs (see the transit function) in a neighborhood (see the neighbors function).
Args:
seed: The point with NULL cost.
iterations: The maximum number of iterations. If ommitted, the costs of all the points of the grid will be computed.
Returns:
The costs map: <points coordinates> => <cost>
"""
costs = {}
# Make a priority queue of considered nodes.
class Prioritize(PriorityQueue):
def __init__(self, keyf):
super().__init__()
self.keyf = keyf
def push(self, i):
# Put (priority,item)
super().put( (self.keyf(i), i) )
def pop(self):
# Return and remove
return super().get()[1]
on_cost = lambda n: costs[n]
front = Prioritize(on_cost)
# Start the front from the seed
costs[seed] = 0
front.push(seed)
i=0
while i < iterations and not front.empty():
print("{} iterations".format(i), end='\r')
# Accept the node with the min cost and update neighbors.
# Accept the considered node with the min cost.
accepted = front.pop()
# Consider neighbors of the accepted node.
around = neighbors(accepted)
assert(len(around)>0)
for n in around:
# If no cost has been computed (i.e. the node is "open").
if n not in costs:
# Compute costs.
costs[n] = transit_func(n, neighbors(n), costs)
front.push(n)
i += 1
return costs
def grid_print(grid, pmin, pmax, step,
out = sys.stdout, sep = " ", end = "\n",
width = 5, fill = ' ', prec = 3):
"""Pretty print a cost map"""
print(" x:", end="", file=out)
for px in range(x(pmin),x(pmax),step):
print("{sep}{:{fill}>{width}}".format(px,sep=sep,fill=fill,width=width,prec=prec), end="", file=out)
print(end=end)
print(" y",end=end)
for py in range(y(pmax),y(pmin),-step):
print("{:{fill}>{width}}:".format(py,fill=fill,width=width,prec=prec), end="", file=out)
for px in range(x(pmin),x(pmax),step):
p=(px,py)
if p in grid:
print("{sep}{:{fill}>{width}.{prec}}".format(float(grid[p]),sep=sep,fill=fill,width=width,prec=prec), end="", file=out)
else:
print("{sep}{:{fill}>{width}}".format(".",sep=sep,fill=fill,width=width,prec=prec), end="", file=out)
print(end=end)
print(end=end)

64
python/functional.py Normal file
View file

@ -0,0 +1,64 @@
import code
def neighbors(op, pmin, pmax, grid_step, **kwargs):
def f(p):
return op(p, pmin, pmax, grid_step, **kwargs)
return f
def quad_grid(p, pmin, pmax, grid_step):
directions = ((1,0),(0,-1),(-1,0),(0,1))
return code.neighbors_grid(p, grid_step, pmin, pmax, directions)
def octo_grid(p, pmin, pmax, grid_step):
directions = ((1,0),(1,-1),(0,-1),(-1,-1),(-1,0),(-1,1),(0,1),(1,1))
return code.neighbors_grid(p, grid_step, pmin, pmax, directions)
def transit(op, **kwargs):
def f(p, neighbors, costs):
return op(p, neighbors, costs, **kwargs)
return f
def on_edge(p, neighbors, costs):
return code.transit_on_edge(p, neighbors, costs)
def in_simplex(p, neighbors, costs, epsilon):
return code.transit_in_simplex(p, neighbors, costs, epsilon)
def algo(neighbors, transit, seed, iterations):
return code.algo_run( seed, iterations, neighbors, transit)
if __name__ == "__main__":
seed = (0,0)
pmin = (-5,-5)
pmax = (15,15)
step = 1
maxit = 300
eps = 1/100
four = neighbors(quad_grid, pmin, pmax, step)
eight = neighbors(octo_grid, pmin, pmax, step)
graph = transit(on_edge)
mesh = transit(in_simplex, epsilon=eps)
print("Dijkstra, 4 neighbors")
cd4 = algo(four, graph, seed, maxit)
code.grid_print(cd4, pmin, pmax, step)
print("Fast marching, 4 neighbors")
cfm4 = algo(four, mesh, seed, maxit)
code.grid_print(cfm4, pmin, pmax, step)
print("Dijkstra, 8 neighbors")
cd8 = algo(eight, graph, seed, maxit)
code.grid_print(cd8, pmin, pmax, step)
print("Fast marching, 8 neighbors")
cfm8 = algo(eight, mesh, seed, maxit)
code.grid_print(cfm8, pmin, pmax, step)

101
python/strategy.py Normal file
View file

@ -0,0 +1,101 @@
import abc
import code
class neighbors:
class Neighborhood(object, metaclass=abc.ABCMeta):
def __init__(self, pmin, pmax):
self.pmin = pmin
self.pmax = pmax
@abc.abstractmethod
def __call__(self, p):
raise NotImplementedError
class quad_grid(Neighborhood):
def __init__(self, grid_step, pmin, pmax):
super().__init__(pmin, pmax)
self.grid_step = grid_step
def __call__(self, p):
directions = ((1,0),(0,-1),(-1,0),(0,1))
return code.neighbors_grid(p, self.grid_step, pmin, pmax, directions)
class octo_grid(Neighborhood):
def __init__(self, grid_step, pmin, pmax):
super().__init__(pmin, pmax)
self.grid_step = grid_step
def __call__(self, p):
directions = ((1,0),(1,-1),(0,-1),(-1,-1),(-1,0),(-1,1),(0,1),(1,1))
return code.neighbors_grid(p, self.grid_step, pmin, pmax, directions)
class transit:
class HopfLax(object, metaclass=abc.ABCMeta):
@abc.abstractmethod
def __call__(self, p, neighbors, costs):
raise NotImplementedError
class on_edge(HopfLax):
def __call__(self, p, neighbors, costs):
return code.transit_on_edge(p, neighbors, costs)
class in_simplex(HopfLax):
def __init__(self, epsilon):
self.eps = epsilon
def __call__(self, p, neighbors, costs):
return code.transit_in_simplex(p, neighbors, costs, self.eps)
class algo(object):
def __init__(self, neighbors, transit):
self.neighbors = neighbors
self.transit = transit
def __call__(self, seed, iterations):
return code.algo_run( seed, iterations, self.neighbors, self.transit)
if __name__ == "__main__":
seed = (0,0)
pmin = (-5,-5)
pmax = (15,15)
step = 1
maxit = 300
eps = 1/100
four = neighbors.quad_grid(step, pmin, pmax)
eight = neighbors.octo_grid(step, pmin, pmax)
graph = transit.on_edge()
mesh = transit.in_simplex(epsilon=eps)
dijkstra4 = algo(four, graph)
print("Dijkstra, 4 neighbors")
cd4 = dijkstra4(seed, maxit)
code.grid_print(cd4, pmin, pmax, step)
fast_marching4 = algo(four, mesh)
print("Fast marching, 4 neighbors")
cfm4 = fast_marching4(seed, maxit)
code.grid_print(cfm4, pmin, pmax, step)
dijkstra8 = algo(eight, graph)
print("Dijkstra, 8 neighbors")
cd8 = dijkstra8(seed, maxit)
code.grid_print(cd8, pmin, pmax, step)
fast_marching8 = algo(eight, mesh)
print("Fast marching, 8 neighbors")
cfm8 = fast_marching8(seed, maxit)
code.grid_print(cfm8, pmin, pmax, step)