def __init__(self, objective, sampler=None, **kwds): """response surface interpolator, where data is sampled from objective Input: objective: function of the form z=f(x) sampler: mystic.search.Searcher instance Additional Inputs: maxpts: int, maximum number of points to use from (x,z) noise: float, amplitude of gaussian noise to remove duplicate x method: string for kind of interpolator dim: number of parameters in the input for the objective function filter: a data filter produced with mystic.filters.generate_filter penalty: mystic.penalty instance of the form y' = k*p(x) constraints: mystic.constraints instance of the form x' = c(x) NOTE: if scipy is not installed, will use np.interp for 1D (non-rbf), or mystic's rbf otherwise. default method is 'nearest' for 1D and 'linear' otherwise. method can be one of ('rbf','linear', 'nearest','cubic','inverse','gaussian','quintic','thin_plate'). """ # sampler configuration from mystic.search import Searcher self.sampler = Searcher() if sampler is None else sampler self.maxpts = kwds.pop('maxpts', None) # N = 1000 self.noise = kwds.pop('noise', 1e-8) self.filter = kwds.pop('filter', None) self.penalty = kwds.pop('penalty', None) self.constraints = kwds.pop('constraints', None) # monitor, archive, and trajectories self._minmon = self._maxmon = None #XXX: better default? self._minarch = self._maxarch = None #XXX: better default? self.x = None # params (x) self.z = None # cost (objective(x)) # point generator(s) and interpolated model(s) #XXX: better names? self.dim = kwds.pop('dim', None) #XXX: should be (or set) len(x) self.objective = objective # original F(x) self.surrogate = None # interpolated F(*x) # interpolator configuration self.args = {} #dict(smooth=0, function='thin_plate') self.args.update(kwds) return
def __init__(self, objective, sampler=None, **kwds): # sampler configuration from mystic.search import Searcher self.sampler = Searcher() if sampler is None else sampler self.maxpts = kwds.pop('maxpts', None) # N = 1000 self.noise = kwds.pop('noise', 1e-8) # monitor, archive, and trajectories self._minmon = self._maxmon = None #XXX: better default? self._minarch = self._maxarch = None #XXX: better default? self.x = None # params (x) self.z = None # cost (objective(x)) # point generator(s) and interpolated model(s) #XXX: better names? self.dim = kwds.pop('dim', None) #XXX: should be (or set) len(x) self.objective = objective # original F(x) self.surrogate = None # interpolated F(*x) # interpolator configuration self.args = {} #dict(smooth=0, function='thin_plate') self.args.update(kwds) return
def __init__(self, objective, sampler=None, **kwds): # sampler configuration from mystic.search import Searcher self.sampler = Searcher() if sampler is None else sampler self.maxpts = kwds.pop('maxpts', None) # N = 1000 self.noise = kwds.pop('noise', 1e-8) # monitor, archive, and trajectories self._minmon = self._maxmon = None #XXX: better default? self._minarch = self._maxarch = None #XXX: better default? self.x = None # params (x) self.z = None # cost (objective(x)) # point generator(s) and interpolated model(s) #XXX: better names? self.dim = kwds.pop('dim', None) #XXX: should be (or set) len(x) self.objective = objective # original F(x) self.surrogate = None # interpolated F(*x) # interpolator configuration self.args = {}#dict(smooth=0, function='thin_plate') self.args.update(kwds) return
def __init__(self, objective, sampler=None, **kwds): """response surface interpolator, where data is sampled from objective Input: objective: function of the form z=f(x) sampler: mystic.search.Searcher instance Additional Inputs: maxpts: int, maximum number of points to use from (x,z) noise: float, amplitude of gaussian noise to remove duplicate x method: string for kind of interpolator dim: number of parameters in the input for the objective function NOTE: if scipy is not installed, will use np.interp for 1D (non-rbf), or mystic's rbf otherwise. default method is 'nearest' for 1D and 'linear' otherwise. method can be one of ('rbf','linear', 'nearest','cubic','inverse','gaussian','quintic','thin_plate'). """ # sampler configuration from mystic.search import Searcher self.sampler = Searcher() if sampler is None else sampler self.maxpts = kwds.pop('maxpts', None) # N = 1000 self.noise = kwds.pop('noise', 1e-8) # monitor, archive, and trajectories self._minmon = self._maxmon = None #XXX: better default? self._minarch = self._maxarch = None #XXX: better default? self.x = None # params (x) self.z = None # cost (objective(x)) # point generator(s) and interpolated model(s) #XXX: better names? self.dim = kwds.pop('dim', None) #XXX: should be (or set) len(x) self.objective = objective # original F(x) self.surrogate = None # interpolated F(*x) # interpolator configuration self.args = {}#dict(smooth=0, function='thin_plate') self.args.update(kwds) return
class Surface(object): #FIXME: should be subclass of Interpolator (?) #surface has: # args - interpolation configuration (smooth, function, ...) # sampler - a search algorithm # maxpts - a maximum number of sampling points # noise - a noise coefficient # dim - dimensionality of the model # function - target function [F(*x)] # objective - target function [F(x)] # surrogate - interpolated function [F(*x)] # model - interpolated function [F(x)] # x,y,z - sampled points(*) # #surface (or sampler) has: # _minmon,_maxmon - step monitor(*) # _minarch,_maxarch - sampled point archives(*) # #surface can: # Sample - populate sampled point archive with solver trajectories # Interpolate - build interpolated function from sampled points # Plot - plot sampled points and interpolated surface # #surface (or sampler) can: # UseMonitor - track trajectories with a monitor(s) # UseArchive - track sampled points in an archive(s) # _max - fetch (x,y,z,model(x,y)) for maximal z of sampled points # _min - fetch (x,y,z,model(x,y)) for minimal z of sampled points def __init__(self, objective, sampler=None, **kwds): """response surface interpolator, where data is sampled from objective Input: objective: function of the form z=f(x) sampler: mystic.search.Searcher instance Additional Inputs: maxpts: int, maximum number of points to use from (x,z) noise: float, amplitude of gaussian noise to remove duplicate x method: string for kind of interpolator dim: number of parameters in the input for the objective function filter: a data filter produced with mystic.filters.generate_filter penalty: mystic.penalty instance of the form y' = k*p(x) constraints: mystic.constraints instance of the form x' = c(x) NOTE: if scipy is not installed, will use np.interp for 1D (non-rbf), or mystic's rbf otherwise. default method is 'nearest' for 1D and 'linear' otherwise. method can be one of ('rbf','linear', 'nearest','cubic','inverse','gaussian','quintic','thin_plate'). """ # sampler configuration from mystic.search import Searcher self.sampler = Searcher() if sampler is None else sampler self.maxpts = kwds.pop('maxpts', None) # N = 1000 self.noise = kwds.pop('noise', 1e-8) self.filter = kwds.pop('filter', None) self.penalty = kwds.pop('penalty', None) self.constraints = kwds.pop('constraints', None) # monitor, archive, and trajectories self._minmon = self._maxmon = None #XXX: better default? self._minarch = self._maxarch = None #XXX: better default? self.x = None # params (x) self.z = None # cost (objective(x)) # point generator(s) and interpolated model(s) #XXX: better names? self.dim = kwds.pop('dim', None) #XXX: should be (or set) len(x) self.objective = objective # original F(x) self.surrogate = None # interpolated F(*x) # interpolator configuration self.args = {} #dict(smooth=0, function='thin_plate') self.args.update(kwds) return # XXX: useful? # def _invert_model: # takes model and returns inverted_model (maxmodel or invmodel?) # def _invert_trajectories: # takes (xyz) trajectories and returns inverted trajectories (xy-z) def UseMonitor(self, min=None, max=None): """track parameter trajectories with a monitor(s) Input: min: monitor instance to track minima; if True, use a new Monitor max: monitor instance to track maxima; if True, use a new Monitor Output: None """ from mystic.monitors import Monitor if type(min) is bool: self._minmon = Monitor() if min else None elif min is not None: self._minmon = min if type(max) is bool: self._maxmon = Monitor() if max else None elif max is not None: self._maxmon = max return def UseArchive(self, min=None, max=None): """track sampled points in an archive(s) Input: min: archive instance to store minima; if True, use a new archive max: archive instance to store maxima; if True, use a new archive Output: None """ from klepto.archives import dict_archive as d if type(min) is bool: self._minarch = d(cached=False) if min else None elif min is not None: self._minarch = min if type(max) is bool: self._maxarch = d(cached=False) if max else None elif max is not None: self._maxarch = max return """ def doit(self, bounds, stop, step=200, scale=False, shift=False, density=9, axes=(), vals=(), maxpts=maxpts, **kwds): if not self.sampler.traj: self.sampler.UseTrajectories() # get trajectories self.Sample(bounds, stop) # get interpolated function self.Interpolate(**kwds) # check extrema #XXX: put _min,_max in Interpolate? (downsampled) f = lambda x,z: (z,surface.surrogate(*x)) print("min: {}; min@f: {}".format(*f(*surface._min()))) print("max: {}; max@f: {}".format(*f(*urfacef._max()))) # plot surface self.Plot(step=step, scale=scale, shift=shift, density=density, axes=axes, vals=vals, maxpts=maxpts) return """ def Sample(self, bounds, stop, clear=False, verbose=False, all=False, **kwds): """sample data (x,z) using objective function z=f(x) Input: bounds: tuple of floats (min,max), bounds on the search region stop: termination condition clear: if True, clear the archive of stored points verbose: if True, print a summary of search/sampling results all: if True, use solver EvalMonitor, else use StepMonitor filter: a data filter produced with mystic.filters.generate_filter penalty: mystic.penalty instance of the form y' = k*p(x) constraints: mystic.constraints instance of the form x' = c(x) Output: x: an array of shape (npts, dim) or (npts,) z: an array of shape (npts,) """ #XXX: does the strategy of finding min/max always apply? import numpy as np penalty = kwds.get('penalty', self.penalty) constraints = kwds.get('constraints', self.constraints) kwargs = dict(stop=stop, penalty=penalty, constraints=constraints) # get model (for minima) model = self.objective self.dim = len(bounds) ### get mins ### monitor = self._minmon archive = None if clear else self._minarch inverse = False if all: #FIXME: better define role/use of Reset/Archive/clear... self.sampler.Reset(None, inv=inverse) # reset the sampler self.sampler.archive = archive self.sampler.Search(model, bounds, evalmon=monitor, **kwargs) else: self.sampler.Reset(archive, inv=inverse) # reset the sampler self.sampler.Search(model, bounds, monitor=monitor, **kwargs) if verbose: self.sampler._summarize() # read trajectories from log (or monitor) xyz = self.sampler.Samples(all=all) if clear: self.sampler.Reset() # reset the sampler ### end mins ### # invert model (for maxima) imodel = lambda *args, **kwds: -model(*args, **kwds) if penalty is not None: # also invert penalty kwargs['penalty'] = lambda *args, **kwds: -penalty(*args, **kwds) ### get maxs ### monitor = self._maxmon archive = None if clear else self._maxarch inverse = True if all: #FIXME: better define role/use of Reset/Archive/clear... self.sampler.Reset(None, inv=inverse) # reset the sampler self.sampler.archive = archive self.sampler.Search(imodel, bounds, evalmon=monitor, **kwargs) else: self.sampler.Reset(archive, inv=inverse) # reset the sampler self.sampler.Search(imodel, bounds, monitor=monitor, **kwargs) if verbose: self.sampler._summarize() xyz = np.hstack((xyz, self.sampler.Samples(all=all))) if clear: self.sampler.Reset() # reset the sampler ### end maxs ### # split into params and cost self.x = xyz.T[:, :-1] self.z = xyz.T[:, -1] # apply any filter, and return filter = kwds.pop('filter', self.filter) if filter: #XXX: better here, or in Interpolate??? self.x, self.z = filter(self.x, self.z) return self.x, self.z def Interpolate(self, **kwds): #XXX: refactor so use self.interpolator ? """interpolate data (x,z) to generate response function z=f(*x) Input: maxpts: int, maximum number of points to use from (x,z) noise: float, amplitude of gaussian noise to remove duplicate x method: string for kind of interpolator extrap: if True, extrapolate a bounding box (can reduce # of nans) arrays: if True, return a numpy array; otherwise don't return arrays Output: interpolated response function, where z=f(*x.T) NOTE: if scipy is not installed, will use np.interp for 1D (non-rbf), or mystic's rbf otherwise. default method is 'nearest' for 1D and 'linear' otherwise. method can be one of ('rbf','linear', 'nearest','cubic','inverse','gaussian','quintic','thin_plate'). """ from interpolator import Interpolator args = self.args.copy() args.update(kwds) maxpts, noise = self.maxpts, self.noise ii = Interpolator(self.x, self.z, maxpts=maxpts, noise=noise, **args) self.surrogate = ii.Interpolate(**args) # build the surrogate self.surrogate.__doc__ = self.objective.__doc__ return self.surrogate def _max(self): #XXX: remove? """get the x[i],z[i] corresponding to the max(z) """ import numpy as np mz = np.argmax(self.z) return self.x[mz], self.z[mz] def _min(self): #XXX: remove? """get the x[i],z[i] corresponding to the min(z) """ import numpy as np mz = np.argmin(self.z) return self.x[mz], self.z[mz] def Plot(self, **kwds): """produce a scatterplot of (x,z) and the surface z = function(*x.T) Input: step: int, plot every 'step' points on the grid [default: 200] scale: float, scaling factor for the z-axis [default: False] shift: float, additive shift for the z-axis [default: False] density: int, density of wireframe for the plot surface [default: 9] axes: tuple, indicies of the axes to plot [default: ()] vals: list of values (one per axis) for unplotted axes [default: ()] maxpts: int, maximum number of (x,z) points to use [default: None] kernel: function transforming x to x', where x' = kernel(x) vtol: float, maximum distance outside bounds hypercube to plot data """ # get interpolted function fx = self.surrogate # plot interpolated surface from plotter import Plotter p = Plotter(self.x, self.z, fx, **kwds) p.Plot() # if plotter interpolated the function, get the function self.surrogate = fx or p.function def __set_function(self, function): #XXX: deal w/ selector (2D)? ExtraArgs? # convert to 'model' format (i.e. takes a parameter vector) from mystic.math.interpolate import _to_objective _objective = _to_objective(function) def objective(x, *args, **kwds): result = _objective(x, *args, **kwds) return result.tolist() if hasattr(result, 'tolist') else result self.objective = objective self.objective.__doc__ = function.__doc__ return def __function(self): #XXX: deal w/ selector (2D)? ExtraArgs? _to_function # convert model to 'args' format (i.e. takes positional args) from mystic.math.interpolate import _to_function function = _to_function(self.objective, ndim=self.dim) function.__doc__ = self.objective.__doc__ return function def __model(self): #XXX: deal w/ selector (2D)? ExtraArgs? _to_objective # convert to 'model' format (i.e. takes a parameter vector) if self.surrogate is None: return None from mystic.math.interpolate import _to_objective _objective = _to_objective(self.surrogate) def objective(x, *args, **kwds): result = _objective(x, *args, **kwds) return result.tolist() if hasattr(result, 'tolist') else result objective.__doc__ = self.objective.__doc__ return objective # interface function = property(__function, __set_function) model = property(__model)
class Surface(object): #surface has: # args - interpolation configuration (smooth, function, ...) # sampler - a search algorithm # maxpts - a maximum number of sampling points # noise - a noise coefficient # dim - dimensionality of the model # function - target function [F(*x)] # objective - target function [F(x)] # surrogate - interpolated function [F(*x)] # model - interpolated function [F(x)] # x,y,z - sampled points(*) # #surface (or sampler) has: # _minmon,_maxmon - step monitor(*) # _minarch,_maxarch - sampled point archives(*) # #surface can: # Sample - populate sampled point archive with solver trajectories # Interpolate - build interpolated function from sampled points # Plot - plot sampled points and interpolated surface # #surface (or sampler) can: # UseMonitor - track trajectories with a monitor(s) # UseArchive - track sampled points in an archive(s) # _noise - remove duplicate sampled points (x) by adding noise to x # _downsample - skip sampled points at a regular interval (for speed) # _max - fetch (x,y,z,model(x,y)) for maximal z of sampled points # _min - fetch (x,y,z,model(x,y)) for minimal z of sampled points def __init__(self, objective, sampler=None, **kwds): # sampler configuration from mystic.search import Searcher self.sampler = Searcher() if sampler is None else sampler self.maxpts = kwds.pop('maxpts', None) # N = 1000 self.noise = kwds.pop('noise', 1e-8) # monitor, archive, and trajectories self._minmon = self._maxmon = None #XXX: better default? self._minarch = self._maxarch = None #XXX: better default? self.x = None # params (x) self.z = None # cost (objective(x)) # point generator(s) and interpolated model(s) #XXX: better names? self.dim = kwds.pop('dim', None) #XXX: should be (or set) len(x) self.objective = objective # original F(x) self.surrogate = None # interpolated F(*x) # interpolator configuration self.args = {} #dict(smooth=0, function='thin_plate') self.args.update(kwds) return # XXX: useful? # def _invert_model: # takes model and returns inverted_model (maxmodel or invmodel?) # def _invert_trajectories: # takes (xyz) trajectories and returns inverted trajectories (xy-z) def UseMonitor(self, min=None, max=None): from mystic.monitors import Monitor if type(min) is bool: self._minmon = Monitor() if min else None elif min is not None: self._minmon = min if type(max) is bool: self._maxmon = Monitor() if max else None elif max is not None: self._maxmon = max return def UseArchive(self, min=None, max=None): from klepto.archives import dict_archive as d if type(min) is bool: self._minarch = d(cached=False) if min else None elif min is not None: self._minarch = min if type(max) is bool: self._maxarch = d(cached=False) if max else None elif max is not None: self._maxarch = max return """ def doit(self, bounds, stop, step=200, scale=False, shift=False, density=9, axes=(), vals=(), maxpts=maxpts, **kwds): if not self.sampler.traj: self.sampler.UseTrajectories() # get trajectories self.Sample(bounds, stop) # get interpolated function self.Interpolate(**kwds) # check extrema #XXX: put _min,_max in Interpolate? (downsampled) f = lambda x,z: (z,surface.surrogate(*x)) print("min: {}; min@f: {}".format(*f(*surface._min()))) print("max: {}; max@f: {}".format(*f(*urfacef._max()))) # plot surface self.Plot(step, scale, shift, density, axes, vals, maxpts) return """ def Sample(self, bounds, stop, clear=False, verbose=False): #XXX: does the strategy of finding min/max always apply? import numpy as np # get model (for minima) model = self.objective self.dim = len(bounds) ### get mins ### stepmon = self._minmon archive = None if clear else self._minarch inverse = False self.sampler.Reset(archive, inv=inverse) # reset the sampler self.sampler.Search(model, bounds, stop=stop, monitor=stepmon) if verbose: self.sampler._summarize() # read trajectories from log (or monitor) xyz = self.sampler.Samples() if clear: self.sampler.Reset() # reset the sampler ### end mins ### # invert model (for maxima) imodel = lambda *args, **kwds: -model(*args, **kwds) ### get maxs ### stepmon = self._maxmon archive = None if clear else self._maxarch inverse = True self.sampler.Reset(archive, inv=inverse) # reset the sampler self.sampler.Search(imodel, bounds, stop=stop, monitor=stepmon) if verbose: self.sampler._summarize() xyz = np.hstack((xyz, self.sampler.Samples())) if clear: self.sampler.Reset() # reset the sampler ### end maxs ### # split into params and cost self.x = xyz.T[:, :-1] self.z = xyz.T[:, -1] return self.x, self.z def _noise(self, scale=None, x=None): #HACK: remove any duplicate points by adding noise import numpy as np if x is None: x = self.x if scale is None: scale = self.noise if not scale: return x return x + np.random.normal(scale=scale, size=x.shape) def _downsample(self, maxpts=None, x=None, z=None): if maxpts is None: maxpts = self.maxpts if x is None: x = self.x if z is None: z = self.z if len(x) != len(z): raise ValueError("the input array lengths must match exactly") if maxpts is not None and len(z) > maxpts: N = max(int(round(len(z) / float(maxpts))), 1) # print("for speed, sampling {} down to {}".format(len(z),len(z)/N)) # ax.plot(x[:,0], x[:,1], z, 'ko', linewidth=2, markersize=4) x = x[::N] z = z[::N] # plt.show() # exit() return x, z def _interpolate(self, x, z, **kwds): import numpy as np from scipy.interpolate import Rbf as interpolate return interpolate(*np.vstack((x.T, z)), **kwds) def Interpolate(self, **kwds): #XXX: better take a strategy? maxpts = kwds.pop('maxpts', self.maxpts) noise = kwds.pop('noise', self.noise) args = self.args.copy() args.update(kwds) x, z = self._downsample(maxpts) #NOTE: really only need to add noise when have duplicate x,y coords x = self._noise(noise, x) # build the surrogate self.surrogate = self._interpolate(x, z, **args) self.surrogate.__doc__ = self.function.__doc__ return self.surrogate def _max(self): import numpy as np x = self.x z = self.z mz = np.argmax(z) return x[mz], z[mz] def _min(self): import numpy as np x = self.x z = self.z mz = np.argmin(z) return x[mz], z[mz] def Plot(self, step=200, scale=False, shift=False, \ density=9, axes=(), vals=(), maxpts=None): # plot interpolated surface from mpl_toolkits.mplot3d import axes3d import matplotlib.pyplot as plt from matplotlib import cm import numpy as np figure = plt.figure() kwds = {'projection': '3d'} ax = figure.gca(**kwds) ax.autoscale(tight=True) if maxpts is None: maxpts = self.maxpts x, z = self._downsample(maxpts) # get two axes to plot, and indices of the remaining axes axes = axes[:2] #XXX: error if wrong size? ix = [i for i in range(len(x.T)) if i not in axes] n = 2 - len(axes) axes, ix = list(axes) + ix[:n], ix[n:] # build list of fixed values (default mins), override with user input #fix = np.zeros(len(ix)) fix = enumerate(self._min()[0]) fix = np.array(tuple(j for (i, j) in fix if i not in axes)) fix[:len(vals)] = vals # build grid of points, one for each param, apply fixed values grid = np.ones((len(x.T), step, step)) grid[ix] = fix[:, None, None] del ix, fix # build sub-surface of surrogate(x) to display, apply to the grid xy = x.T[axes] M = complex('{}j'.format(step)) grid[axes] = np.mgrid[xy[0].min():xy[0].max():M, xy[1].min():xy[1].max():M] del xy # evaluate the surrogate on the sub-surface z_ = self.surrogate(*grid) # scaling used by model plotter if scale: if shift: z_ = np.asarray(z_) + shift z_ = np.log(4 * np.asarray(z_) * scale + 1) + 2 # plot surface d = max(11 - density, 1) x_ = grid[axes[0]] y_ = grid[axes[-1]] ax.plot_wireframe(x_, y_, z_, rstride=d, cstride=d) #ax.plot_surface(x_, y_, z_, rstride=d, cstride=d, cmap=cm.jet, linewidth=0, antialiased=False) # use the sampled values z_ = z # scaling used by model plotter if scale: if shift: z_ = np.asarray(z_) + shift z_ = np.log(4 * np.asarray(z_) * scale + 1) + 2 # plot data points x_ = x.T[axes[0]] y_ = x.T[axes[-1]] ax.plot(x_, y_, z_, 'ko', linewidth=2, markersize=4) plt.show() #XXX: show or don't show?... or return? # figure.savefig('griewangk.png') def __set_function(self, function): #XXX: deal w/ selector (2D)? ExtraArgs? # convert to 'model' format (i.e. takes a parameter vector) def objective(x, *args, **kwds): return function(*(tuple(x) + args), **kwds).tolist() self.objective = objective self.objective.__doc__ = function.__doc__ return def __function(self): #XXX: deal w/ selector (2D)? ExtraArgs? # convert model to 'args' format (i.e. takes positional args) def function(*args, **kwds): len = self.dim # or kwds.pop('len', None) if len is None: return self.objective(args, **kwds) return self.objective(args[:len], *args[len:], **kwds) function.__doc__ = self.objective.__doc__ return function def __model(self): #XXX: deal w/ selector (2D)? ExtraArgs? # convert to 'model' format (i.e. takes a parameter vector) if self.surrogate is None: return None def objective(x, *args, **kwds): return self.surrogate(*(tuple(x) + args), **kwds).tolist() objective.__doc__ = self.objective.__doc__ return objective # interface function = property(__function, __set_function) model = property(__model)
class Surface(object): #surface has: # args - interpolation configuration (smooth, function, ...) # sampler - a search algorithm # maxpts - a maximum number of sampling points # noise - a noise coefficient # dim - dimensionality of the model # function - target function [F(*x)] # objective - target function [F(x)] # surrogate - interpolated function [F(*x)] # model - interpolated function [F(x)] # x,y,z - sampled points(*) # #surface (or sampler) has: # _minmon,_maxmon - step monitor(*) # _minarch,_maxarch - sampled point archives(*) # #surface can: # Sample - populate sampled point archive with solver trajectories # Interpolate - build interpolated function from sampled points # Plot - plot sampled points and interpolated surface # #surface (or sampler) can: # UseMonitor - track trajectories with a monitor(s) # UseArchive - track sampled points in an archive(s) # _noise - remove duplicate sampled points (x) by adding noise to x # _downsample - skip sampled points at a regular interval (for speed) # _max - fetch (x,y,z,model(x,y)) for maximal z of sampled points # _min - fetch (x,y,z,model(x,y)) for minimal z of sampled points def __init__(self, objective, sampler=None, **kwds): # sampler configuration from mystic.search import Searcher self.sampler = Searcher() if sampler is None else sampler self.maxpts = kwds.pop('maxpts', None) # N = 1000 self.noise = kwds.pop('noise', 1e-8) # monitor, archive, and trajectories self._minmon = self._maxmon = None #XXX: better default? self._minarch = self._maxarch = None #XXX: better default? self.x = None # params (x) self.z = None # cost (objective(x)) # point generator(s) and interpolated model(s) #XXX: better names? self.dim = kwds.pop('dim', None) #XXX: should be (or set) len(x) self.objective = objective # original F(x) self.surrogate = None # interpolated F(*x) # interpolator configuration self.args = {}#dict(smooth=0, function='thin_plate') self.args.update(kwds) return # XXX: useful? # def _invert_model: # takes model and returns inverted_model (maxmodel or invmodel?) # def _invert_trajectories: # takes (xyz) trajectories and returns inverted trajectories (xy-z) def UseMonitor(self, min=None, max=None): from mystic.monitors import Monitor if type(min) is bool: self._minmon = Monitor() if min else None elif min is not None: self._minmon = min if type(max) is bool: self._maxmon = Monitor() if max else None elif max is not None: self._maxmon = max return def UseArchive(self, min=None, max=None): from klepto.archives import dict_archive as d if type(min) is bool: self._minarch = d(cached=False) if min else None elif min is not None: self._minarch = min if type(max) is bool: self._maxarch = d(cached=False) if max else None elif max is not None: self._maxarch = max return """ def doit(self, bounds, stop, step=200, scale=False, shift=False, density=9, axes=(), vals=(), maxpts=maxpts, **kwds): if not self.sampler.traj: self.sampler.UseTrajectories() # get trajectories self.Sample(bounds, stop) # get interpolated function self.Interpolate(**kwds) # check extrema #XXX: put _min,_max in Interpolate? (downsampled) f = lambda x,z: (z,surface.surrogate(*x)) print "min: {}; min@f: {}".format(*f(*surface._min())) print "max: {}; max@f: {}".format(*f(*urfacef._max())) # plot surface self.Plot(step, scale, shift, density, axes, vals, maxpts) return """ def Sample(self, bounds, stop, clear=False, verbose=False): #XXX: does the strategy of finding min/max always apply? import numpy as np # get model (for minima) model = self.objective self.dim = len(bounds) ### get mins ### stepmon = self._minmon archive = None if clear else self._minarch inverse = False self.sampler.Reset(archive, inv=inverse) # reset the sampler self.sampler.Search(model, bounds, stop=stop, monitor=stepmon) if verbose: self.sampler._summarize() # read trajectories from log (or monitor) xyz = self.sampler.Samples() if clear: self.sampler.Reset() # reset the sampler ### end mins ### # invert model (for maxima) imodel = lambda *args, **kwds: -model(*args, **kwds) ### get maxs ### stepmon = self._maxmon archive = None if clear else self._maxarch inverse = True self.sampler.Reset(archive, inv=inverse) # reset the sampler self.sampler.Search(imodel, bounds, stop=stop, monitor=stepmon) if verbose: self.sampler._summarize() xyz = np.hstack((xyz, self.sampler.Samples())) if clear: self.sampler.Reset() # reset the sampler ### end maxs ### # split into params and cost self.x = xyz.T[:,:-1] self.z = xyz.T[:,-1] return self.x, self.z def _noise(self, scale=None, x=None): #HACK: remove any duplicate points by adding noise import numpy as np if x is None: x = self.x if scale is None: scale = self.noise if not scale: return x return x + np.random.normal(scale=scale, size=x.shape) def _downsample(self, maxpts=None, x=None, z=None): if maxpts is None: maxpts = self.maxpts if x is None: x = self.x if z is None: z = self.z if len(x) != len(z): raise ValueError("the input array lengths must match exactly") if maxpts is not None and len(z) > maxpts: N = max(int(round(len(z)/float(maxpts))),1) # print "for speed, sampling {} down to {}".format(len(z),len(z)/N) # ax.plot(x[:,0], x[:,1], z, 'ko', linewidth=2, markersize=4) x = x[::N] z = z[::N] # plt.show() # exit() return x, z def _interpolate(self, x, z, **kwds): import numpy as np from scipy.interpolate import Rbf as interpolate return interpolate(*np.vstack((x.T, z)), **kwds) def Interpolate(self, **kwds): #XXX: better take a strategy? maxpts = kwds.pop('maxpts', self.maxpts) noise = kwds.pop('noise', self.noise) args = self.args.copy() args.update(kwds) x, z = self._downsample(maxpts) #NOTE: really only need to add noise when have duplicate x,y coords x = self._noise(noise, x) # build the surrogate self.surrogate = self._interpolate(x, z, **args) self.surrogate.__doc__ = self.function.__doc__ return self.surrogate def _max(self): import numpy as np x = self.x z = self.z mz = np.argmax(z) return x[mz],z[mz] def _min(self): import numpy as np x = self.x z = self.z mz = np.argmin(z) return x[mz],z[mz] def Plot(self, step=200, scale=False, shift=False, \ density=9, axes=(), vals=(), maxpts=None): # plot interpolated surface from mpl_toolkits.mplot3d import axes3d import matplotlib.pyplot as plt from matplotlib import cm import numpy as np figure = plt.figure() kwds = {'projection':'3d'} ax = figure.gca(**kwds) ax.autoscale(tight=True) if maxpts is None: maxpts = self.maxpts x, z = self._downsample(maxpts) # get two axes to plot, and indices of the remaining axes axes = axes[:2] #XXX: error if wrong size? ix = [i for i in range(len(x.T)) if i not in axes] n = 2-len(axes) axes, ix = list(axes)+ix[:n], ix[n:] # build list of fixed values (default mins), override with user input #fix = np.zeros(len(ix)) fix = enumerate(self._min()[0]) fix = np.array(tuple(j for (i,j) in fix if i not in axes)) fix[:len(vals)] = vals # build grid of points, one for each param, apply fixed values grid = np.ones((len(x.T),step,step)) grid[ix] = fix[:,None,None] del ix, fix # build sub-surface of surrogate(x) to display, apply to the grid xy = x.T[axes] M = complex('{}j'.format(step)) grid[axes] = np.mgrid[xy[0].min():xy[0].max():M, xy[1].min():xy[1].max():M] del xy # evaluate the surrogate on the sub-surface z_ = self.surrogate(*grid) # scaling used by model plotter if scale: if shift: z_ = np.asarray(z_)+shift z_ = np.log(4*np.asarray(z_)*scale+1)+2 # plot surface d = max(11 - density, 1) x_ = grid[axes[0]] y_ = grid[axes[-1]] ax.plot_wireframe(x_, y_, z_, rstride=d, cstride=d) #ax.plot_surface(x_, y_, z_, rstride=d, cstride=d, cmap=cm.jet, linewidth=0, antialiased=False) # use the sampled values z_ = z # scaling used by model plotter if scale: if shift: z_ = np.asarray(z_)+shift z_ = np.log(4*np.asarray(z_)*scale+1)+2 # plot data points x_ = x.T[axes[0]] y_ = x.T[axes[-1]] ax.plot(x_, y_, z_, 'ko', linewidth=2, markersize=4) plt.show() #XXX: show or don't show?... or return? # figure.savefig('griewangk.png') def __set_function(self, function): #XXX: deal w/ selector (2D)? ExtraArgs? # convert to 'model' format (i.e. takes a parameter vector) def objective(x, *args, **kwds): return function(*(tuple(x)+args), **kwds).tolist() self.objective = objective self.objective.__doc__ = function.__doc__ return def __function(self): #XXX: deal w/ selector (2D)? ExtraArgs? # convert model to 'args' format (i.e. takes positional args) def function(*args, **kwds): len = self.dim # or kwds.pop('len', None) if len is None: return self.objective(args, **kwds) return self.objective(args[:len], *args[len:], **kwds) function.__doc__ = self.objective.__doc__ return function def __model(self): #XXX: deal w/ selector (2D)? ExtraArgs? # convert to 'model' format (i.e. takes a parameter vector) if self.surrogate is None: return None def objective(x, *args, **kwds): return self.surrogate(*(tuple(x)+args), **kwds).tolist() objective.__doc__ = self.objective.__doc__ return objective # interface function = property(__function, __set_function ) model = property(__model )
#CUTE: 'configure' monitor and archive if they are desired if stepmon: stepmon = LoggingMonitor(1) # montor for all runs itermon = LoggingMonitor(1, filename='inv.txt') #XXX: log.txt? else: stepmon = itermon = None if archive: #python2.5 ar_name = '__%s_%sD_cache__' % (model.__self__.__class__.__name__,ndim) archive = dir_archive(ar_name, serialized=True, cached=False) ar_name = '__%s_%sD_invcache__' % (model.__self__.__class__.__name__,ndim) ivcache = dir_archive(ar_name, serialized=True, cached=False) else: archive = ivcache = None from mystic.search import Searcher #XXX: init w/ archive, then UseArchive? sampler = Searcher(npts, retry, tol, mem, _map, archive, sprayer, seeker) sampler.Verbose(disp) sampler.UseTrajectories(traj) ### doit ### maxpts = 1000. #10000. surface = Surface(model, sampler, maxpts=maxpts, dim=ndim) surface.UseMonitor(stepmon, itermon) surface.UseArchive(archive, ivcache) density = 9 shift = 0 scale = 0 step = 200 args = { 'smooth': 0,
seeker = PowellDirectionalSolver npts = 25 # number of solvers _map = Pool().map retry = 1 # max consectutive iteration retries without a cache 'miss' tol = 8 # rounding precision mem = 1 # cache rounding precision #CUTE: 'configure' monitor and archive if they are desired if stepmon: stepmon = LoggingMonitor(1) # montor for all runs else: stepmon = None if archive: #python2.5 ar_name = '__%s_%sD_cache__' % (model.__self__.__class__.__name__,ndim) archive = dir_archive(ar_name, serialized=True, cached=False) else: archive = None searcher = Searcher(npts, retry, tol, mem, _map, archive, sprayer, seeker) searcher.Verbose(disp) searcher.UseTrajectories(traj) searcher.Reset(archive, inv=False) searcher.Search(model, bounds, stop=stop, monitor=stepmon) searcher._summarize() ##### extract results ##### xyz = searcher.Samples() ##### invert the model, and get the maxima ##### imodel = lambda *args, **kwds: -model(*args, **kwds) #CUTE: 'configure' monitor and archive if they are desired if stepmon not in (None, False):
repeat = 0 # number of times to repeat the search tol = 8 # rounding precision mem = 1 # cache rounding precision size = 0 # max in-memory cache size #CUTE: 'configure' monitor and archive if they are desired if evalmon: evalmon = LoggingMonitor(1) # montor for all runs else: evalmon = None if archive: #python2.5 name = getattr(model,'__name__','model') ar_name = '__%s_%sD_cache__' % (name,ndim) archive = dir_archive(ar_name, serialized=True, cached=False) else: archive = None # configure a Searcher to use a "evaluation archive" searcher = Searcher(npts, retry, tol, mem, size, _map, archive, None, sprayer, seeker, repeat=repeat) searcher.Verbose(disp) searcher.UseTrajectories(traj) searcher.Reset(None, inv=False) #XXX: careful, can replace searcher.cache searcher.Search(model, bounds, stop=stop, evalmon=evalmon) searcher._summarize() ##### extract results ##### xyz = searcher.Samples(all=True) ##### invert the model, and get the maxima ##### imodel = lambda *args, **kwds: -model(*args, **kwds) #CUTE: 'configure' monitor and archive if they are desired if evalmon not in (None, False):
if stepmon: stepmon = LoggingMonitor(1) # montor for all runs else: stepmon = None if archive: #python2.5 name = getattr(model, '__name__', 'model') ar_name = '__%s_%sD_cache__' % (name, ndim) archive = dir_archive(ar_name, serialized=True, cached=False) else: archive = None # configure a Searcher to use a "trajectory cache" searcher = Searcher(npts, retry, tol, mem, size, _map, None, archive, sprayer, seeker, repeat=repeat) searcher.Verbose(disp) searcher.UseTrajectories(traj) searcher.Reset(archive, inv=False) searcher.Search(model, bounds, stop=stop, monitor=stepmon) searcher._summarize() ##### extract results ##### xyz = searcher.Samples()
class Surface(object): #FIXME: should be subclass of Interpolator (?) #surface has: # args - interpolation configuration (smooth, function, ...) # sampler - a search algorithm # maxpts - a maximum number of sampling points # noise - a noise coefficient # dim - dimensionality of the model # function - target function [F(*x)] # objective - target function [F(x)] # surrogate - interpolated function [F(*x)] # model - interpolated function [F(x)] # x,y,z - sampled points(*) # #surface (or sampler) has: # _minmon,_maxmon - step monitor(*) # _minarch,_maxarch - sampled point archives(*) # #surface can: # Sample - populate sampled point archive with solver trajectories # Interpolate - build interpolated function from sampled points # Plot - plot sampled points and interpolated surface # #surface (or sampler) can: # UseMonitor - track trajectories with a monitor(s) # UseArchive - track sampled points in an archive(s) # _max - fetch (x,y,z,model(x,y)) for maximal z of sampled points # _min - fetch (x,y,z,model(x,y)) for minimal z of sampled points def __init__(self, objective, sampler=None, **kwds): """response surface interpolator, where data is sampled from objective Input: objective: function of the form z=f(x) sampler: mystic.search.Searcher instance Additional Inputs: maxpts: int, maximum number of points to use from (x,z) noise: float, amplitude of gaussian noise to remove duplicate x method: string for kind of interpolator dim: number of parameters in the input for the objective function NOTE: if scipy is not installed, will use np.interp for 1D (non-rbf), or mystic's rbf otherwise. default method is 'nearest' for 1D and 'linear' otherwise. method can be one of ('rbf','linear', 'nearest','cubic','inverse','gaussian','quintic','thin_plate'). """ # sampler configuration from mystic.search import Searcher self.sampler = Searcher() if sampler is None else sampler self.maxpts = kwds.pop('maxpts', None) # N = 1000 self.noise = kwds.pop('noise', 1e-8) # monitor, archive, and trajectories self._minmon = self._maxmon = None #XXX: better default? self._minarch = self._maxarch = None #XXX: better default? self.x = None # params (x) self.z = None # cost (objective(x)) # point generator(s) and interpolated model(s) #XXX: better names? self.dim = kwds.pop('dim', None) #XXX: should be (or set) len(x) self.objective = objective # original F(x) self.surrogate = None # interpolated F(*x) # interpolator configuration self.args = {}#dict(smooth=0, function='thin_plate') self.args.update(kwds) return # XXX: useful? # def _invert_model: # takes model and returns inverted_model (maxmodel or invmodel?) # def _invert_trajectories: # takes (xyz) trajectories and returns inverted trajectories (xy-z) def UseMonitor(self, min=None, max=None): """track parameter trajectories with a monitor(s) Input: min: monitor instance to track minima; if True, use a new Monitor max: monitor instance to track maxima; if True, use a new Monitor Output: None """ from mystic.monitors import Monitor if type(min) is bool: self._minmon = Monitor() if min else None elif min is not None: self._minmon = min if type(max) is bool: self._maxmon = Monitor() if max else None elif max is not None: self._maxmon = max return def UseArchive(self, min=None, max=None): """track sampled points in an archive(s) Input: min: archive instance to store minima; if True, use a new archive max: archive instance to store maxima; if True, use a new archive Output: None """ from klepto.archives import dict_archive as d if type(min) is bool: self._minarch = d(cached=False) if min else None elif min is not None: self._minarch = min if type(max) is bool: self._maxarch = d(cached=False) if max else None elif max is not None: self._maxarch = max return """ def doit(self, bounds, stop, step=200, scale=False, shift=False, density=9, axes=(), vals=(), maxpts=maxpts, **kwds): if not self.sampler.traj: self.sampler.UseTrajectories() # get trajectories self.Sample(bounds, stop) # get interpolated function self.Interpolate(**kwds) # check extrema #XXX: put _min,_max in Interpolate? (downsampled) f = lambda x,z: (z,surface.surrogate(*x)) print("min: {}; min@f: {}".format(*f(*surface._min()))) print("max: {}; max@f: {}".format(*f(*urfacef._max()))) # plot surface self.Plot(step=step, scale=scale, shift=shift, density=density, axes=axes, vals=vals, maxpts=maxpts) return """ def Sample(self, bounds, stop, clear=False, verbose=False, all=False): """sample data (x,z) using objective function z=f(x) Input: bounds: tuple of floats (min,max), bounds on the search region stop: termination condition clear: if True, clear the archive of stored points verbose: if True, print a summary of search/sampling results all: if True, use solver EvalMonitor, else use StepMonitor Output: x: an array of shape (npts, dim) or (npts,) z: an array of shape (npts,) """ #XXX: does the strategy of finding min/max always apply? import numpy as np # get model (for minima) model = self.objective self.dim = len(bounds) ### get mins ### monitor = self._minmon archive = None if clear else self._minarch inverse = False if all: #FIXME: better define role/use of Reset/Archive/clear... self.sampler.Reset(None, inv=inverse) # reset the sampler self.sampler.archive = archive self.sampler.Search(model, bounds, stop=stop, evalmon=monitor) else: self.sampler.Reset(archive, inv=inverse) # reset the sampler self.sampler.Search(model, bounds, stop=stop, monitor=monitor) if verbose: self.sampler._summarize() # read trajectories from log (or monitor) xyz = self.sampler.Samples(all=all) if clear: self.sampler.Reset() # reset the sampler ### end mins ### # invert model (for maxima) imodel = lambda *args, **kwds: -model(*args, **kwds) ### get maxs ### monitor = self._maxmon archive = None if clear else self._maxarch inverse = True if all: #FIXME: better define role/use of Reset/Archive/clear... self.sampler.Reset(None, inv=inverse) # reset the sampler self.sampler.archive = archive self.sampler.Search(imodel, bounds, stop=stop, evalmon=monitor) else: self.sampler.Reset(archive, inv=inverse) # reset the sampler self.sampler.Search(imodel, bounds, stop=stop, monitor=monitor) if verbose: self.sampler._summarize() xyz = np.hstack((xyz, self.sampler.Samples(all=all))) if clear: self.sampler.Reset() # reset the sampler ### end maxs ### # split into params and cost self.x = xyz.T[:,:-1] self.z = xyz.T[:,-1] return self.x, self.z def Interpolate(self, **kwds): #XXX: refactor so use self.interpolator ? """interpolate data (x,z) to generate response function z=f(*x) Input: maxpts: int, maximum number of points to use from (x,z) noise: float, amplitude of gaussian noise to remove duplicate x method: string for kind of interpolator extrap: if True, extrapolate a bounding box (can reduce # of nans) arrays: if True, return a numpy array; otherwise don't return arrays Output: interpolated response function, where z=f(*x.T) NOTE: if scipy is not installed, will use np.interp for 1D (non-rbf), or mystic's rbf otherwise. default method is 'nearest' for 1D and 'linear' otherwise. method can be one of ('rbf','linear', 'nearest','cubic','inverse','gaussian','quintic','thin_plate'). """ from interpolator import Interpolator args = self.args.copy() args.update(kwds) maxpts, noise = self.maxpts, self.noise ii = Interpolator(self.x, self.z, maxpts=maxpts, noise=noise, **args) self.surrogate = ii.Interpolate(**args) # build the surrogate self.surrogate.__doc__ = self.objective.__doc__ return self.surrogate def _max(self): #XXX: remove? """get the x[i],z[i] corresponding to the max(z) """ import numpy as np mz = np.argmax(self.z) return self.x[mz], self.z[mz] def _min(self): #XXX: remove? """get the x[i],z[i] corresponding to the min(z) """ import numpy as np mz = np.argmin(self.z) return self.x[mz], self.z[mz] def Plot(self, **kwds): """produce a scatterplot of (x,z) and the surface z = function(*x.T) Input: step: int, plot every 'step' points on the grid [default: 200] scale: float, scaling factor for the z-axis [default: False] shift: float, additive shift for the z-axis [default: False] density: int, density of wireframe for the plot surface [default: 9] axes: tuple, indicies of the axes to plot [default: ()] vals: list of values (one per axis) for unplotted axes [default: ()] maxpts: int, maximum number of (x,z) points to use [default: None] kernel: function transforming x to x', where x' = kernel(x) """ # get interpolted function fx = self.surrogate # plot interpolated surface from plotter import Plotter p = Plotter(self.x, self.z, fx, **kwds) p.Plot() # if plotter interpolated the function, get the function self.surrogate = fx or p.function def __set_function(self, function): #XXX: deal w/ selector (2D)? ExtraArgs? # convert to 'model' format (i.e. takes a parameter vector) from mystic.math.interpolate import _to_objective _objective = _to_objective(function) def objective(x, *args, **kwds): result = _objective(x, *args, **kwds) return result.tolist() if hasattr(result, 'tolist') else result self.objective = objective self.objective.__doc__ = function.__doc__ return def __function(self): #XXX: deal w/ selector (2D)? ExtraArgs? _to_function # convert model to 'args' format (i.e. takes positional args) from mystic.math.interpolate import _to_function function = _to_function(self.objective, ndim=self.dim) function.__doc__ = self.objective.__doc__ return function def __model(self): #XXX: deal w/ selector (2D)? ExtraArgs? _to_objective # convert to 'model' format (i.e. takes a parameter vector) if self.surrogate is None: return None from mystic.math.interpolate import _to_objective _objective = _to_objective(self.surrogate) def objective(x, *args, **kwds): result = _objective(x, *args, **kwds) return result.tolist() if hasattr(result, 'tolist') else result objective.__doc__ = self.objective.__doc__ return objective # interface function = property(__function, __set_function ) model = property(__model )
costmon = LoggingMonitor(1, filename='inv.txt') #XXX: log.txt? else: monitor = costmon = None if archive: #python2.5 name = getattr(model,'__name__','model') ar_name = '__%s_%sD_cache__' % (name,ndim) archive = dir_archive(ar_name, serialized=True, cached=False) ar_name = '__%s_%sD_invcache__' % (name,ndim) ivcache = dir_archive(ar_name, serialized=True, cached=False) else: archive = ivcache = None from mystic.search import Searcher #XXX: init w/ archive, then UseArchive? expts,evals = (None,archive) if all else (archive, None) #expts,evals = (archive, None) #XXX: don't override the sample archive sampler = Searcher(npts, retry, tol, mem, size, _map, evals, expts, sprayer, seeker, repeat=repeat) sampler.Verbose(disp) sampler.UseTrajectories(traj) ### doit ### maxpts = 1000. #10000. surface = Surface(model, sampler, maxpts=maxpts, dim=ndim) surface.UseMonitor(monitor, costmon) surface.UseArchive(archive, ivcache) density = 9 shift = 0 scale = 0 step = 200 args = { #'smooth': 0,
if evalmon: evalmon = LoggingMonitor(1) # montor for all runs else: evalmon = None if archive: #python2.5 name = getattr(model, '__name__', 'model') ar_name = '__%s_%sD_cache__' % (name, ndim) archive = dir_archive(ar_name, serialized=True, cached=False) else: archive = None # configure a Searcher to use a "evaluation archive" searcher = Searcher(npts, retry, tol, mem, size, _map, archive, None, sprayer, seeker, repeat=repeat) searcher.Verbose(disp) searcher.UseTrajectories(traj) searcher.Reset(None, inv=False) #XXX: careful, can replace searcher.cache searcher.Search(model, bounds, stop=stop, evalmon=evalmon) searcher._summarize() ##### extract results ##### xyz = searcher.Samples(all=True)