first version
This commit is contained in:
commit
624c92e934
10 changed files with 1333 additions and 0 deletions
206
python/code.py
Normal file
206
python/code.py
Normal 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)
|
||||
Loading…
Add table
Add a link
Reference in a new issue