def plot(self, x, y, z=None, ep=None, **kwargs): if np.any(ep): zern = Zernike(ep) fig, ax = zern.plot(x, y, **kwargs) elif np.any(z): fig, ax = self.zern.plot(x, y, z=z, **kwargs) else: fig, ax = self.zern.plot(x, y, **kwargs) return fig, ax
class GradientDissenter(object): def __init__(self, emunup, Imax=15, Jmax=21, Kmax=3): self.emunup = emunup self.zern = Zernike(self.emunup[0][0]) self.Mu = self.emunup.shape[0] self.Nu = self.emunup.shape[1] self.Pmax = self.emunup.shape[2] self.Imax = Imax self.Jmax = Jmax self.Kmax = Kmax # get relevant matrices self.beta, self.alphaJ = self.make_matrices(self.Imax, self.Jmax) self.ndim = self.Imax * (self.Jmax + self.Kmax * self.Nu) + self.Mu p0 = 1e-2 * np.random.randn(self.ndim) p0[:3] = 1 self.kmu, self.bij, self.cnuik = self.p_to_matrices(p0) self.loss_history = [np.inf] # number, not object self.kmus = [self.kmu.copy()] self.bijs = [self.bij.copy()] self.cnuiks = [self.cnuik.copy()] self.logs = [[np.inf] * 3] self.ehat = fast_zernike.fast_calc(self.kmu, self.bij, self.cnuik, self.beta, self.alphaJ) self.ehats = [self.ehat.copy()] @classmethod def make_matrices(cls, Imax, Jmax): Zern = Zernike(Imax) gamma_x = Zern.gamma_x gamma_y = Zern.gamma_y alpha_I = Zern.mult_matrix # do multiplication of gammas and alpha to beta # TODO: Might have to flip gamma's # beta^mu_il = sum_kn gamma_ki gamma_nl alpha_kn1 # where K < I Nmax = gamma_x.shape[0] bxx = np.dot(gamma_x.T, np.dot(gamma_x.T, alpha_I[:Nmax, :Nmax])) byy = np.dot(gamma_y.T, np.dot(gamma_y.T, alpha_I[:Nmax, :Nmax])) bxy = np.dot(gamma_x.T, np.dot(gamma_y.T, alpha_I[:Nmax, :Nmax])) beta0 = (bxx + byy)[:,:,0] beta1 = (bxx - byy)[:,:,0] beta2 = (2 * bxy)[:,:,0] beta = np.array([beta0, beta1, beta2]) alphaJ = Zernike(Jmax).mult_matrix return beta, alphaJ def plot(self, x, y, z=None, ep=None, **kwargs): if np.any(ep): zern = Zernike(ep) fig, ax = zern.plot(x, y, **kwargs) elif np.any(z): fig, ax = self.zern.plot(x, y, z=z, **kwargs) else: fig, ax = self.zern.plot(x, y, **kwargs) return fig, ax def p_to_matrices(self, p): """ We have the following things: K^mu b_{ij} c^nu_{ik} mu \in (0, 2] nu \in (0, Nexposures] i \in (0, Imax] # number of zernikes to represent j \in (0, Jmax] # number of field zernikes k \in (0, Kmax] # number of field corrections """ kmu = p[:self.Mu] bij = p[self.Mu: self.Jmax * self.Imax + self.Mu] cnuik = p[self.Jmax * self.Imax + self.Mu: (self.Jmax * self.Imax + self.Mu) + self.Imax * self.Kmax * self.Nu] bij = bij.reshape(self.Imax, self.Jmax) cnuik = cnuik.reshape(self.Nu, self.Imax, self.Kmax) return kmu, bij, cnuik def fit(self, xtol=1e-8, ftol=1e-6, maxiter=10000, step_size=None, maxfun=None, verbose=False, learning_rate_decay=0, **kwargs): """ ala scipy.optimize: xtol : float, optional Relative error in xopt acceptable for convergence. ftol : number, optional Relative error in func(xopt) acceptable for convergence. maxiter : int, optional Maximum number of iterations to perform. maxfun : number, optional Maximum number of function evaluations to make. TODO: Currently not implimented learning_rate_decay : if step_size is specified, after every update, multiply step_size by (1 - learning_rate_decay) """ # TODO: incorporate several different parameter update modes """ if update == 'sgd': dx = -learning_rate * grads[p] elif update == 'momentum': if not p in self.step_cache: self.step_cache[p] = np.zeros(grads[p].shape) dx = np.zeros_like(grads[p]) # you can remove this after dx = momentum * self.step_cache[p] - learning_rate * grads[p] self.step_cache[p] = dx elif update == 'rmsprop': decay_rate = 0.99 # you could also make this an option if not p in self.step_cache: self.step_cache[p] = np.zeros(grads[p].shape) dx = np.zeros_like(grads[p]) # you can remove this after self.step_cache[p] = self.step_cache[p] * decay_rate + (1.0 - decay_rate) * grads[p] ** 2 dx = -(learning_rate * grads[p]) / np.sqrt(self.step_cache[p] + 1e-8) """ for it in xrange(maxiter): if verbose: print(it, self.loss_history[-1]) lnlike_old = self.loss_history[-1] self.ehat = fast_zernike.fast_calc(self.kmu, self.bij, self.cnuik, self.beta, self.alphaJ) self.dlogldb = fast_zernike.fast_calc_dlogldb(self.kmu, self.bij, self.cnuik, self.emunup, self.ehat, self.beta, self.alphaJ) self.dlogldc = fast_zernike.fast_calc_dlogldc(self.kmu, self.bij, self.cnuik, self.emunup, self.ehat, self.beta, self.alphaJ) self.dlogldk = fast_zernike.fast_calc_dlogldk(self.kmu, self.bij, self.cnuik, self.emunup, self.ehat, self.beta, self.alphaJ) lnlike = np.mean(np.square(self.emunup - self.ehat)) if lnlike - lnlike_old > 1e-2 * lnlike_old: print(it, lnlike) return # apply derivatives self.kmus.append(self.kmu.copy()) self.bijs.append(self.bij.copy()) self.cnuiks.append(self.cnuik.copy()) self.ehats.append(self.ehat.copy()) self.logs.append([self.dlogldb.copy(), self.dlogldc.copy(), self.dlogldk.copy()]) self.kmu -= step_size * self.dlogldk self.cnuik -= step_size * self.dlogldc self.bij -= step_size * self.dlogldb self.loss_history.append(lnlike) if type(step_size) != type(None): step_size *= 1 - learning_rate_decay # check changes if np.abs((lnlike - lnlike_old) / lnlike) < ftol: print ('ftol reached') return # if np.all(asdf < xtol): # print ('xtol reached') # return print('maxiter reached') return