def update(self, params, values): """ Update the particles field given new parameter values """ #1. Figure out if we're going to do a global update, in which # case we just draw from scratch. global_update, particles = self._update_type(params) # if we are doing a global update, everything must change, so # starting fresh will be faster instead of add subtract if global_update: self.set_values(params, values) self.initialize() return # otherwise, update individual particles. delete the current versions # of the particles update the particles, and redraw them anew at the # places given by (params, values) oldargs = self._drawargs() for n in particles: self._draw_particle(self.pos[n], *listify(oldargs[n]), sign=-1) self.set_values(params, values) newargs = self._drawargs() for n in particles: self._draw_particle(self.pos[n], *listify(newargs[n]), sign=+1)
def split_params(self, params, values=None): """ Split params, values into groups that correspond to the ordering in self.comps. For example, given a sphere collection and slab:: [ (spheres) [pos rad etc] [pos val, rad val, etc] (slab) [slab params] [slab vals] ] """ pc, vc = [], [] returnvalues = values is not None if values is None: values = [0] * len(util.listify(params)) for c in self.comps: tp, tv = [], [] for p, v in zip(util.listify(params), util.listify(values)): if not p in self.lmap: raise NotAParameterError("%r does not belong to %r" % (p, self)) if c in self.pmap[p]: tp.append(p) tv.append(v) pc.append(tp) vc.append(tv) if returnvalues: return pc, vc return pc
def update(self, params, values): """Calls an update, but clips radii to be > 0""" # radparams = self.param_radii() params = listify(params) values = listify(values) for i, p in enumerate(params): # if (p in radparams) & (values[i] < 0): if (p[-2:] == '-a') and (values[i] < 0): values[i] = 0.0 super(PlatonicSpheresCollection, self).update(params, values)
def set_values(self, params, values): """ Directly set the values corresponding to certain parameters. This does not necessarily trigger and update of the calculation, See also -------- :func:`~peri.comp.comp.ParameterGroup.update` : full update func """ for p, v in zip(util.listify(params), util.listify(values)): self.param_dict[p] = v
def set_values(self, params, values): for p, v in zip(listify(params), listify(values)): typ, ind = self._p2i(p) if typ == 'zscale': self.zscale = v elif typ == 'x': self.pos[ind][2] = v elif typ == 'y': self.pos[ind][1] = v elif typ == 'z': self.pos[ind][0] = v elif typ == 'a': self.rad[ind] = v
def add_particle(self, pos, rad): """ Add a particle or list of particles given by a list of positions and radii, both need to be array-like. Parameters ---------- pos : array-like [N, 3] Positions of all new particles rad : array-like [N] Corresponding radii of new particles Returns ------- inds : N-element numpy.ndarray. Indices of the added particles. """ rad = listify(rad) # add some zero mass particles to the list (same as not having these # particles in the image, which is true at this moment) inds = np.arange(self.N, self.N + len(rad)) self.pos = np.vstack([self.pos, pos]) self.rad = np.hstack([self.rad, np.zeros(len(rad))]) # update the parameters globally self.setup_variables() self.trigger_parameter_change() # now request a drawing of the particle plz params = self.param_particle_rad(inds) self.trigger_update(params, rad) return inds
def remove_particle(self, inds): """ Remove the particle at index `inds`, may be a list. Returns [3,N], [N] element numpy.ndarray of pos, rad. """ if self.rad.shape[0] == 0: return inds = listify(inds) # Here's the game plan: # 1. get all positions and sizes of particles that we will be # removing (to return to user) # 2. redraw those particles to 0.0 radius # 3. remove the particles and trigger changes # However, there is an issue -- if there are two particles at opposite # ends of the image, it will be significantly slower than usual pos = self.pos[inds].copy() rad = self.rad[inds].copy() self.trigger_update(self.param_particle_rad(inds), np.zeros(len(inds))) self.pos = np.delete(self.pos, inds, axis=0) self.rad = np.delete(self.rad, inds, axis=0) # update the parameters globally self.setup_variables() self.trigger_parameter_change() return np.array(pos).reshape(-1, 3), np.array(rad).reshape(-1)
def _hess(self, funct, params=None, dl=2e-5, rts=False, **kwargs): """ Hessian of a `func` wrt to parmaeters `params`. (see _graddoc) """ if params is None: params = self.param_all() ps = util.listify(params) f0 = funct(**kwargs) # get the shape of the entire hessian, allocate an array shape = f0.shape if isinstance(f0, np.ndarray) else (1, ) shape = (len(ps), len(ps)) + shape hess = np.zeros(shape) for i, pi in enumerate(ps): for j, pj in enumerate(ps[i:]): J = j + i thess = self._hess_two_param(funct, pi, pj, dl=dl, rts=rts, **kwargs) hess[i][J] = thess hess[J][i] = thess return np.squeeze(hess)
def get_values(self, params): vals = [] for p in util.listify(params): if not p in self.lmap: raise NotAParameterError("%r does not belong to %r" % (p, self)) vals.append(self.lmap[p][0].get_values(p)) return util.delistify(vals, params)
def update(self, params, values): params = util.listify(params) values = util.listify(values) if len(params) < len(self.params) / 2: for p, v1 in zip(params, values): v0 = self.get_values(p) tm = self.param_term[p] self.field -= v0 * self.term(tm) self.set_values(p, v1) self.field += v1 * self.term(tm) else: self.set_values(params, values) self.field = np.zeros(self.shape.shape, dtype=self.float_precision) for p, v in zip(self.params, self.values): self.field += v * self.term(self.param_term[p])
def _update_type(self, params): """ Returns dozscale and particle list of update """ dozscale = False particles = [] for p in listify(params): typ, ind = self._p2i(p) particles.append(ind) dozscale = dozscale or typ == 'zscale' particles = set(particles) return dozscale, particles
def update(self, params, values): op = {'*': mul, '+': add}[self.op] params = util.listify(params) values = util.listify(values) if len(params) < len(self.params) / 2: for p, v1 in zip(params, values): if p in self.poly_params: tm = self._term(self.poly_params[p]) v0 = self.get_values(p) self.poly += (v1 - v0) * tm self.set_values(params, values) self.field = self.calc_field() else: self.set_values(params, values) self.poly = self.calc_poly() self.field = self.calc_field()
def get_update_tile(self, params, values): #a lot of this is duplicated from parent to here if not self.local_updates: return self.shape.copy() params = util.listify(params) values = util.listify(values) c = self.category # check for global update requiring parameters: for p in params: if p in self.poly_params or p == c + '-scale' or p == c + '-off': return self.shape.copy() # now look for the local update sizes orig_values = self.get_values(params) #see for loop... tiles = [] for p, v in zip(params, values): # figure out the barnes local update size # looks like matt does this by changing each param, then seeing # manually which points have changed.... if not p in self.barnes_params: raise RuntimeError('Im confused...') val0 = self._barnes(self.b_out) self.set_values(p, v) val1 = self._barnes(self.b_out) inds = np.arange(self.b_out.shape[0]) inds = inds[np.abs(val1 - val0) > 1e-12] if len(inds) < 2: continue l, r = inds.min(), inds.max() tile = self.shape.copy() tile.l[2] = l tile.r[2] = r tiles.append(util.Tile(tile.l, tile.r)) # raise NotImplementedError('Local updates not implemented yet') if len(tiles) == 0: return None return util.Tile.boundingtile(tiles)
def get_update_tile(self, params, values): if not self.local_updates: return self.shape.copy() params = util.listify(params) values = util.listify(values) c = self.category # check for global update requiring parameters: for p in params: if p in self.poly_params or p == c + '-scale' or p == c + '-off': return self.shape.copy() # now look for the local update sizes orig_values = self.get_values(params) tiles = [] for p, v in zip(params, values): # figure out the barnes local update size for n, grp in enumerate(self.barnes_params): if not p in grp: continue val0 = self._barnes(self.b_out, n=n) self.set_values(p, v) val1 = self._barnes(self.b_out, n=n) inds = np.arange(self.b_out.shape[0]) inds = inds[np.abs(val1 - val0) > 1e-12] if len(inds) < 2: continue l, r = inds.min(), inds.max() tile = self.shape.copy() tile.l[2] = l tile.r[2] = r tiles.append(util.Tile(tile.l, tile.r)) self.set_values(params, orig_values) if len(tiles) == 0: return None return util.Tile.boundingtile(tiles)
def get_values(self, params): """ Get the value of a list or single parameter. Parameters ---------- params : string, list of string name of parameters which to retrieve """ return util.delistify( [self.param_dict[p] for p in util.listify(params)], params)
def neighbors(self, cutoff, indices=None): """ Returns neighbors within a cutoff for certain particles """ indices = indices if indices is not None else np.arange(self.N) indices = util.listify(indices) neighs = [] for i in indices: rij = self.pos[i] - self.pos dist = np.sqrt((rij**2).sum(axis=-1)) neighs.append(np.arange(self.N)[dist <= cutoff]) return neighs
def set_draw_method(self, method, alpha=None, user_method=None): self.methods = [ 'lerp', 'logistic', 'triangle', 'constrained-cubic', 'exact-gaussian', 'exact-gaussian-trim', 'exact-gaussian-fast', 'user-defined' ] self.sphere_functions = { 'bool': sphere_bool, 'lerp': sphere_lerp, 'logistic': sphere_logistic, 'triangle': sphere_triangle_cdf, 'exact-gaussian': sphere_analytical_gaussian, 'exact-gaussian-trim': sphere_analytical_gaussian_trim, 'exact-gaussian-fast': sphere_analytical_gaussian_fast, 'constrained-cubic': sphere_constrained_cubic } self.alpha_defaults = { 'bool': 0, 'lerp': 0.4539, 'logistic': 6.5, 'triangle': 0.6618, 'exact-gaussian': 0.27595, 'exact-gaussian-trim': 0.27595, 'exact-gaussian-fast': 0.27595, 'constrained-cubic': 0.84990, } if user_method: self.sphere_functions['user-defined'] = user_method[0] self.alpha_defaults['user-defined'] = user_method[1] self.method = method if alpha is not None: self.alpha = tuple(listify(alpha)) else: self.alpha = tuple(listify(self.alpha_defaults[self.method]))
def update(self, params, values): params = util.listify(params) values = util.listify(values) if len(params) < len(self.params) / 2: for p, v1 in zip(params, values): if p in self.xy_param: order = self.xy_param[p] term = self.field_xy else: order = self.z_param[p] term = self.field_z v0 = self.get_values(p) term -= v0 * self.term(order) self.set_values(p, v1) term += v1 * self.term(order) op = {'*': mul, '+': add}[self.operation] self.field[:] = op(self.field_xy, 1.0 + self.field_z) else: self.set_values(params, values) self.field[:] = self.calc_field()
def get_values(self, params): values = [] for p in listify(params): typ, ind = self._p2i(p) if typ == 'zscale': values.append(self.zscale) elif typ == 'x': values.append(self.pos[ind][2]) elif typ == 'y': values.append(self.pos[ind][1]) elif typ == 'z': values.append(self.pos[ind][0]) elif typ == 'a': values.append(self.rad[ind]) return delistify(values, params)
def _grad(self, funct, params=None, dl=2e-5, rts=False, nout=1, out=None, **kwargs): """ Gradient of `func` wrt a set of parameters params. (see _graddoc) """ if params is None: params = self.param_all() ps = util.listify(params) f0 = funct(**kwargs) # get the shape of the entire gradient to return and make an array calc_shape = (lambda ar: (len(ps), ) + (ar.shape if isinstance(ar, np.ndarray) else (1, ))) if out is not None: grad = out # reference elif nout == 1: shape = calc_shape(f0) grad = np.zeros(shape) # must be preallocated for mem reasons else: shape = [calc_shape(f0[i]) for i in xrange(nout)] grad = [np.zeros(shp) for shp in shape] for i, p in enumerate(ps): if nout == 1: grad[i] = self._grad_one_param(funct, p, dl=dl, rts=rts, nout=nout, **kwargs) else: stuff = self._grad_one_param(funct, p, dl=dl, rts=rts, nout=nout, **kwargs) for a in xrange(nout): grad[a][i] = stuff[a] return grad # was np.squeeze(grad)
def sample_state(state, blocks, stepout=1, slicing=True, N=1, doprint=False, procedure='uniform'): eng = engines.SequentialBlockEngine(state) opsay = observers.Printer() ohist = observers.HistogramObserver(block=blocks) eng.add_samplers([ samplers.SliceSampler1D(stepout, block=b, procedure=procedure) for b in util.listify(blocks) ]) eng.add_likelihood_observers(opsay) if doprint else None eng.add_state_observers(ohist) eng.dosteps(N) return ohist
def param_particle_rad(self, ind): """ Get radius of one or more particles """ ind = self._vps(listify(ind)) return [self._i2p(i, 'a') for i in ind]
def _kurtosis_coeffs(self, d): return listify(self.get_values(self.moment_coeffs['kurt'][d]))
def initialize(self): """Start from scratch and initialize all objects / draw self.particles""" self.particles = np.zeros(self.shape.shape, dtype=self.float_precision) for p0, arg0 in zip(self.pos, self._drawargs()): self._draw_particle(p0, *listify(arg0))
def _skew_coeffs(self, d): return listify(self.get_values(self.moment_coeffs['skew'][d]))
def param_particle_pos(self, ind): """ Get position of one or more particles """ ind = self._vps(listify(ind)) return [self._i2p(i, j) for i in ind for j in ['z', 'y', 'x']]
def param_particle_pos(self, ind): """ Get position of one or more particles """ #FIXME assumes 3D and x,y,z labels right now.... ind = self._vps(listify(ind)) return [self._i2p(i, j) for i in ind for j in ['z', 'y', 'x']]
def _sigma_coeffs(self, d=0): return listify(self.get_values(self.poly_coeffs[d]))