Commit a90792a3 by Johann Dreo

first import

parents
requirement.txt 0 → 100644
 numpy matplotlib
sho/__init__.py 0 → 100644
 import numpy as np __all__ = [ 'x', 'y', 'distance', 'algo', 'make', 'iters', 'num', 'bit', 'plot', 'pb', ] ######################################################################## # Utilities ######################################################################## def x(a): """Return the first element of a 2-tuple. >>> x([1,2]) 1 """ return a[0] def y(a): """Return the second element of a 2-tuple. >>> y([1,2]) 2 """ return a[1] def distance(a,b): """Euclidean distance (in pixels). >>> distance( (1,1),(2,2) ) == math.sqrt(2) True """ return np.sqrt( (x(a)-x(b))**2 + (y(a)-y(b))**2 )
sho/algo.py 0 → 100644
 ######################################################################## # Algorithms ######################################################################## import numpy as np def random(func, init, again): """Iterative random search template.""" best_sol = init() best_val = func(best_sol) val,sol = best_val,best_sol i = 0 while again(i, best_val, best_sol): sol = init() val = func(sol) if val >= best_val: best_val = val best_sol = sol i += 1 return best_val, best_sol def greedy(func, init, neighb, again): """Iterative randomized greedy heuristic template.""" best_sol = init() best_val = func(best_sol) val,sol = best_val,best_sol i = 1 while again(i, best_val, best_sol): sol = neighb(best_sol) val = func(sol) # Use >= and not >, so as to avoid random walk on plateus. if val >= best_val: best_val = val best_sol = sol i += 1 return best_val, best_sol # TODO add a simulated-annealing template. # TODO add a population-based stochastic heuristic template.
sho/bit.py 0 → 100644
 import numpy as np import copy from . import x,y,pb ######################################################################## # Objective functions ######################################################################## def cover_sum(sol, domain_width, sensor_range): """Compute the coverage quality of the given array of bits.""" domain = np.zeros((domain_width,domain_width)) sensors = to_sensors(sol) return np.sum(pb.coverage(domain, sensors, sensor_range)) def to_sensors(sol): """Convert an square array of d lines/columns containing n ones to an array of n 2-tuples with related coordinates. >>> to_sensors([[1,0],[1,0]]) [(0, 0), (0, 1)] """ sensors = [] for i in range(len(sol)): for j in range(len(sol[i])): if sol[i][j] == 1: sensors.append( (j,i) ) return sensors ######################################################################## # Initialization ######################################################################## def rand(domain_width, nb_sensors): """"Draw a random domain containing nb_sensors ones.""" domain = np.zeros( (domain_width,domain_width) ) for x,y in np.random.randint(0, domain_width, (nb_sensors, 2)): domain[y][x] = 1 return domain ######################################################################## # Neighborhood ######################################################################## def neighb_square(sol, scale, domain_width): """Draw a random array by moving ones to adjacent cells.""" # Copy, because Python pass by reference # and we may not want to alter the original solution. new = copy.copy(sol) for py in range(len(sol)): for px in range(len(sol[py])): if sol[py][px] == 1: new[py][px] = 0 # Remove original position. d = np.random.randint(-scale//2,scale//2,2) if py+y(d) < 0 : d[1] = np.random.randint(-py,scale//2) if py+y(d) >= domain_width : d[1] = np.random.randint(-scale//2,domain_width-py) if px+y(d) < 0 : d[0] = np.random.randint(0,scale//2) if px+x(d) >= domain_width : d[0] = np.random.randint(-scale//2,domain_width-px) new[py+y(d)][px+x(d)] = 1 return new
sho/iters.py 0 → 100644
 import sys from collections import deque ######################################################################## # Stopping criterions ######################################################################## def max(i, val, sol, nb_it): """Stop after reaching nb_it iterations.""" if i < nb_it: return True else: return False def target(i, val, sol, target): """Stop after reaching target value.""" if val < target: return True else: return False class steady: """Stop if improvement is lesser than epsilon, in the last delta iterations.""" def __init__(self, delta, epsilon = 0): self.epsilon = epsilon self.delta = delta self.delta_vals = deque() def __call__(self, i, val, sol): if i < self.delta: # Always wait the first delta iterations. self.delta_vals.append(val) return True else: #FILO stack. self.delta_vals.popleft() self.delta_vals.append(val) if val - self.delta_vals[0] <= self.epsilon: return False # Stop here. else: return True # Stopping criterions that are actually just checkpoints. def several(i, val, sol, agains): """several stopping criterions in one.""" over = [] for again in agains: over.append( again(i, val, sol) ) return all(over) def save(i, val, sol, filename="run.csv", fmt="{it} ; {val} ; {sol}\n"): """Save all iterations to a file.""" # Append a line at the end of the file. with open(filename.format(it=i), 'a') as fd: fd.write( fmt.format(it=i, val=val, sol=sol) ) return True # No incidence on termination. def history(i, val, sol, history): history.append((val,sol)) return True def log(i, val, sol, fmt="{it} {val}\n"): """Print progress on stderr.""" sys.stderr.write( fmt.format(it=i, val=val) ) return True
sho/make.py 0 → 100644
 """Wrappers that captures parameters of a function and returns an operator with a given interface.""" def func(cover, **kwargs): """Make an objective function from the given function. An objective function takes a solution and returns a scalar.""" def f(sol): return cover(sol,**kwargs) return f def init(init, **kwargs): """Make an initialization operator from the given function. An init. op. returns a solution.""" def f(): return init(**kwargs) return f def neig(neighb, **kwargs): """Make an neighborhood operator from the given function. A neighb. op. takes a solution and returns another one.""" def f(sol): return neighb(sol, **kwargs) return f def iter(iters, **kwargs): """Make an iterations operator from the given function. A iter. op. takes a value and a solution and returns the current number of iterations.""" def f(i, val, sol): return iters(i, val, sol, **kwargs) return f
sho/num.py 0 → 100644
 import numpy as np from . import pb ######################################################################## # Objective functions ######################################################################## # Decoupled from objective functions, so as to be used in display. def to_sensors(sol): """Convert a vector of n*2 dimension to an array of n 2-tuples. >>> to_sensors([0,1,2,3]) [(0, 1), (2, 3)] """ sensors = [] for i in range(0,len(sol),2): sensors.append( ( int(round(sol[i])), int(round(sol[i+1])) ) ) return sensors def cover_sum(sol, domain_width, sensor_range): """Compute the coverage quality of the given vector.""" domain = np.zeros((domain_width,domain_width)) sensors = to_sensors(sol) return np.sum(pb.coverage(domain, sensors, sensor_range)) ######################################################################## # Initialization ######################################################################## def rand(dim, scale): """Draw a random vector in [0,scale]**dim.""" return np.random.random(dim) * scale ######################################################################## # Neighborhood ######################################################################## def neighb_square(sol, scale, domain_width): """Draw a random vector in a square of witdh `scale` around the given one.""" # TODO handle constraints new = sol + (np.random.random(len(sol)) * scale - scale/2) return new
sho/pb.py 0 → 100644
 from . import distance ######################################################################## # Objective functions ######################################################################## def coverage(domain, sensors, sensor_range): """Set a given domain's cells to on if they are visible from one of the given sensors at the given sensor_range. >>> coverage(np.zeros((5,5)),[(2,2)],2) array([[ 0., 0., 0., 0., 0.], [ 0., 1., 1., 1., 0.], [ 0., 1., 1., 1., 0.], [ 0., 1., 1., 1., 0.], [ 0., 0., 0., 0., 0.]]) """ for py in range(len(domain)): for px in range(len(domain[py])): p = (px,py) for x in sensors: if distance(x,p) < sensor_range: domain[py][px] = 1 break return domain def line(x0, y0, x1, y1): """Compute the set of pixels (integer coordinates) of the line between the given line (x0,y0) -> (x1,y1). Use the Bresenham's algorithm. This make a generator that yield the start and the end points. """ dx = x1 - x0 dy = y1 - y0 if dx > 0: xs = 1 else: xs = -1 if dy > 0: ys = 1 else: xs = -1 dx = abs(dx) dy = abs(dy) if dx > dy: ax, xy, yx, ay = xs, 0, 0, ys else: dx, dy = dy, dx ax, xy, yx, ay = 0, ys, xs, 0 D = 2 * dy - dx y = 0 for x in range(dx + 1): yield x0 + x*ax + y*yx , y0 + x*xy + y*ay if D >= 0: y += 1 D -= 2 * dx D += 2 * dy
sho/plot.py 0 → 100644
 import numpy as np from matplotlib import cm import matplotlib.pyplot as plt import itertools from mpl_toolkits.mplot3d import Axes3D from . import x,y,distance def sphere(x,offset=0.5): """Computes the square of a multi-dimensional vector x.""" f = 0 for i in range(len(x)): f += (x[i]-offset)**2 return -1 * f def surface(ax, shape, f): Z = np.zeros( shape ) for y in range(shape[0]): for x in range(shape[1]): Z[y][x] = f( (x,y) ) X = np.arange(0,shape[0],1) Y = np.arange(0,shape[1],1) X,Y = np.meshgrid(X,Y) #ax.plot_surface(X, Y, Z, cmap=cm.viridis) ax.plot_surface(X, Y, Z) def path(ax, shape, history): def pairwise(iterable): a, b = itertools.tee(iterable) next(b, None) return zip(a, b) k=0 for i,j in pairwise(range(len(history)-1)): xi = history[i][1][0] yi = history[i][1][1] zi = history[i][0] xj = history[j][1][0] yj = history[j][1][1] zj = history[j][0] x = [xi, xj] y = [yi, yj] z = [zi, zj] ax.plot(x,y,z, color=cm.RdYlBu(k)) k+=1 def highlight_sensors(domain, sensors, val=2): """Add twos to the given domain, in the cells where the given sensors are located. >>> highlight_sensors( [[0,0],[1,1]], [(0,0),(1,1)] ) [[2, 0], [1, 2]] """ for s in sensors: # `coverage` fills the domain with ones, # adding twos will be visible in an image. domain[y(s)][x(s)] = val return domain
snp.py 0 → 100644