def objfunc_scattering(self, minvec): N = self.prior_next.xdim imvec = minvec[:N**2] EpsilonList = minvec[N**2:] if self.transform_next == 'log': imvec = np.exp(imvec) IM = ehtim.image.Image(imvec.reshape(N,N), self.prior_next.psize, self.prior_next.ra, self.prior_next.dec, rf=self.obs_next.rf, source=self.prior_next.source, mjd=self.prior_next.mjd) scatt_im = self.scattering_model.Scatter(IM, Epsilon_Screen=so.MakeEpsilonScreenFromList(EpsilonList, N), ea_ker = self._ea_ker, sqrtQ=self._sqrtQ, Linearized_Approximation=True).imvec #the scattered image vector # Calculate the chi^2 using the scattered image datterm = 0. chi2_term_dict = self.make_chisq_dict(scatt_im) for dname in sorted(self.dat_term_next.keys()): datterm += self.dat_term_next[dname] * (chi2_term_dict[dname] - 1.) # Calculate the entropy using the unscattered image regterm = 0 reg_term_dict = self.make_reg_dict(imvec) for regname in sorted(self.reg_term_next.keys()): regterm += self.reg_term_next[regname] * reg_term_dict[regname] # Scattering screen regularization term chisq_epsilon = sum(EpsilonList*EpsilonList)/((N*N-1.0)/2.0) regterm_scattering = self.alpha_phi_next * (chisq_epsilon - 1.0) return datterm + regterm + regterm_scattering
def plotcur_scattering(self, minvec): if self._show_updates: N = self.prior_next.xdim imvec = minvec[:N**2] EpsilonList = minvec[N**2:] if self.transform_next == 'log': imvec = np.exp(imvec) IM = ehtim.image.Image(imvec.reshape(N, N), self.prior_next.psize, self.prior_next.ra, self.prior_next.dec, rf=self.obs_next.rf, source=self.prior_next.source, mjd=self.prior_next.mjd) #the scattered image vector scatt_im = self.scattering_model.Scatter( IM, Epsilon_Screen=so.MakeEpsilonScreenFromList(EpsilonList, N), ea_ker=self._ea_ker, sqrtQ=self._sqrtQ, Linearized_Approximation=True).imvec # Calculate the chi^2 using the scattered image datterm = 0. chi2_term_dict = self.make_chisq_dict(scatt_im) for dname in sorted(self.dat_term_next.keys()): datterm += self.dat_term_next[dname] * (chi2_term_dict[dname] - 1.) # Calculate the entropy using the unscattered image regterm = 0 reg_term_dict = self.make_reg_dict(imvec) for regname in sorted(self.reg_term_next.keys()): regterm += self.reg_term_next[regname] * reg_term_dict[regname] # Scattering screen regularization term chisq_epsilon = sum(EpsilonList * EpsilonList) / ( (N * N - 1.0) / 2.0) regterm_scattering = self.alpha_phi_next * (chisq_epsilon - 1.0) outstr = "i: %d " % self._nit for dname in sorted(self.dat_term_next.keys()): outstr += "%s : %0.2f " % (dname, chi2_term_dict[dname]) for regname in sorted(self.reg_term_next.keys()): outstr += "%s : %0.2f " % (regname, reg_term_dict[regname]) outstr += "Epsilon chi^2 : %0.2f " % (chisq_epsilon) outstr += "Max |Epsilon| : %0.2f " % (max(abs(EpsilonList))) print(outstr) self._nit += 1
imgr_deblur.make_image_I() imgr_deblur.out_last().display() # Now image using stochastic optics imgr_so = eh.imager.Imager(obs, gaussprior, prior_im=gaussprior, maxit=200, flux=total_flux, clipfloor=-1.) imgr_so.make_image_I_stochastic_optics() # Now look at the unscattered image, the scattered image, and replicate the scattering using the solved screen imgr_so.out_last().display() imgr_so.out_scattered_last().display() imgr_so.scattering_model.Scatter(imgr_so.out_last(), Epsilon_Screen=so.MakeEpsilonScreenFromList( imgr_so.out_epsilon_last(), npix), ea_ker=imgr_so._ea_ker, sqrtQ=imgr_so._sqrtQ, Linearized_Approximation=True, DisplayImage=True) #Note that only the scattered image will fit the measured visibilities! eh.comp_plots.plotall_obs_im_compare(obs, imgr_so.out_last(), 'uvdist', 'amp') eh.comp_plots.plotall_obs_im_compare(obs, imgr_so.out_scattered_last(), 'uvdist', 'amp') # Decrease the scattering regularization slightly and re-image (desired max |Epsilon| is ~2.5) imgr_so.alpha_phi_next /= 2.0 imgr_so.init_next = imgr_so.out_last().blur_circ(obs.res()) imgr_so.epsilon_list_next = imgr_so.out_epsilon_last() imgr_so.make_image_I_stochastic_optics()
def make_image_I_stochastic_optics(self, grads=True, show_updates=True): """Reconstructs an image of total flux density using the stochastic optics scattering mitigation technique. Uses the scattering model of the imager. If none has been specified, it will default to a standard model for Sgr A*. Returns the estimated unscattered image. Args: grads (bool): Flag for whether or not to use analytic gradients. show_updates (bool): Flag for whether or not to show updates for each step of convergence. Returns: out (Image): The estimated *unscattered* image. """ N = self.prior_next.xdim # Checks and initialize self.check_params() self.check_limits() self.init_imager_I() self.init_imager_scattering() # Generate the initial image+screen vector. By default, the screen is re-initialized to zero each time. if self.transform_next == 'log': xinit = np.log(self._ninit_I) else: xinit = self._ninit_I if len(self.epsilon_list_next) == 0: xinit = np.concatenate((xinit, np.zeros(N**2 - 1))) else: xinit = np.concatenate((xinit, self.epsilon_list_next)) self._nit = 0 # Print stats if show_updates: self._show_updates = True else: self._show_updates = False self.plotcur_scattering(xinit) # Minimize optdict = {'maxiter': self.maxit_next, 'ftol': STOP, 'maxcor': NHIST} tstart = time.time() if grads: res = opt.minimize(self.objfunc_scattering, xinit, method='L-BFGS-B', jac=self.objgrad_scattering, options=optdict, callback=self.plotcur_scattering) else: res = opt.minimize(self.objfunc_scattering, xinit, method='L-BFGS-B', options=optdict, callback=self.plotcur_scattering) tstop = time.time() # Format output out = res.x[:N**2] if self.transform_next == 'log': out = np.exp(out) if np.any(np.invert(self._embed_mask)): raise Exception("Embedding is not currently implemented!") out = embed(out, self._embed_mask) outim = image.Image(out.reshape(N, N), self.prior_next.psize, self.prior_next.ra, self.prior_next.dec, rf=self.prior_next.rf, source=self.prior_next.source, mjd=self.prior_next.mjd, pulse=self.prior_next.pulse) outep = res.x[N**2:] outscatt = self.scattering_model.Scatter( outim, Epsilon_Screen=so.MakeEpsilonScreenFromList(outep, N), ea_ker=self._ea_ker, sqrtQ=self._sqrtQ, Linearized_Approximation=True) # Preserving image complex polarization fractions if len(self.prior_next.qvec): qvec = self.prior_next.qvec * out / self.prior_next.imvec uvec = self.prior_next.uvec * out / self.prior_next.imvec outim.add_qu(qvec.reshape(N, N), uvec.reshape(N, N)) # Print stats print("time: %f s" % (tstop - tstart)) print("J: %f" % res.fun) print(res.message) # Append to history logstr = str(self.nruns) + ": make_image_I_stochastic_optics()" self._append_image_history(outim, logstr) self._out_list_epsilon.append(res.x[N**2:]) self._out_list_scattered.append(outscatt) self.nruns += 1 # Return Image object return outim
def objgrad_scattering(self, minvec): """Current stochastic optics objective function gradient """ wavelength = C / self.obs_next.rf * 100.0 #Observing wavelength [cm] wavelengthbar = wavelength / (2.0 * np.pi) #lambda/(2pi) [cm] N = self.prior_next.xdim #Field of view, in cm, at the scattering screen FOV = self.prior_next.psize * N * self.scattering_model.observer_screen_distance rF = self.scattering_model.rF(wavelength) imvec = minvec[:N**2] EpsilonList = minvec[N**2:] if self.transform_next == 'log': imvec = np.exp(imvec) IM = ehtim.image.Image(imvec.reshape(N, N), self.prior_next.psize, self.prior_next.ra, self.prior_next.dec, rf=self.obs_next.rf, source=self.prior_next.source, mjd=self.prior_next.mjd) #the scattered image vector scatt_im = self.scattering_model.Scatter( IM, Epsilon_Screen=so.MakeEpsilonScreenFromList(EpsilonList, N), ea_ker=self._ea_ker, sqrtQ=self._sqrtQ, Linearized_Approximation=True).imvec EA_Image = self.scattering_model.Ensemble_Average_Blur( IM, ker=self._ea_ker) EA_Gradient = so.Wrapped_Gradient( (EA_Image.imvec / (FOV / N)).reshape(N, N)) #The gradient signs don't actually matter, but let's make them match intuition (i.e., right to left, bottom to top) EA_Gradient_x = -EA_Gradient[1] EA_Gradient_y = -EA_Gradient[0] Epsilon_Screen = so.MakeEpsilonScreenFromList(EpsilonList, N) phi = self.scattering_model.MakePhaseScreen( Epsilon_Screen, IM, obs_frequency_Hz=self.obs_next.rf, sqrtQ_init=self._sqrtQ).imvec.reshape((N, N)) phi_Gradient = so.Wrapped_Gradient(phi / (FOV / N)) phi_Gradient_x = -phi_Gradient[1] phi_Gradient_y = -phi_Gradient[0] #Entropy gradient; wrt unscattered image so unchanged by scattering regterm = 0 reg_term_dict = self.make_reggrad_dict(imvec) for regname in sorted(self.reg_term_next.keys()): regterm += self.reg_term_next[regname] * reg_term_dict[regname] # Chi^2 gradient wrt the unscattered image # First, the chi^2 gradient wrt to the scattered image datterm = 0. chi2_term_dict = self.make_chisqgrad_dict(scatt_im) for dname in sorted(self.dat_term_next.keys()): datterm += self.dat_term_next[dname] * (chi2_term_dict[dname] - 1.) dchisq_dIa = datterm.reshape((N, N)) # Now the chain rule factor to get the chi^2 gradient wrt the unscattered image gx = (rF**2.0 * so.Wrapped_Convolve( self._ea_ker_gradient_x[::-1, ::-1], phi_Gradient_x * (dchisq_dIa))).flatten() gy = (rF**2.0 * so.Wrapped_Convolve( self._ea_ker_gradient_y[::-1, ::-1], phi_Gradient_y * (dchisq_dIa))).flatten() chisq_grad_im = so.Wrapped_Convolve(self._ea_ker[::-1, ::-1], (dchisq_dIa)).flatten() + gx + gy # Gradient of the data chi^2 wrt to the epsilon screen #Preliminary Definitions chisq_grad_epsilon = np.zeros(N**2 - 1) i_grad = 0 ell_mat = np.zeros((N, N)) m_mat = np.zeros((N, N)) for ell in range(0, N): for m in range(0, N): ell_mat[ell, m] = ell m_mat[ell, m] = m #Real part; top row for t in range(1, (N + 1) / 2): s = 0 grad_term = so.Wrapped_Gradient( wavelengthbar / FOV * self._sqrtQ[s][t] * 2.0 * np.cos(2.0 * np.pi / N * (ell_mat * s + m_mat * t)) / (FOV / N)) grad_term_x = -grad_term[1] grad_term_y = -grad_term[0] chisq_grad_epsilon[i_grad] = np.sum( dchisq_dIa * rF**2 * (EA_Gradient_x * grad_term_x + EA_Gradient_y * grad_term_y)) i_grad = i_grad + 1 #Real part; remainder for s in range(1, (N + 1) / 2): for t in range(N): grad_term = so.Wrapped_Gradient( wavelengthbar / FOV * self._sqrtQ[s][t] * 2.0 * np.cos(2.0 * np.pi / N * (ell_mat * s + m_mat * t)) / (FOV / N)) grad_term_x = -grad_term[1] grad_term_y = -grad_term[0] chisq_grad_epsilon[i_grad] = np.sum( dchisq_dIa * rF**2 * (EA_Gradient_x * grad_term_x + EA_Gradient_y * grad_term_y)) i_grad = i_grad + 1 #Imaginary part; top row for t in range(1, (N + 1) / 2): s = 0 grad_term = so.Wrapped_Gradient( -wavelengthbar / FOV * self._sqrtQ[s][t] * 2.0 * np.sin(2.0 * np.pi / N * (ell_mat * s + m_mat * t)) / (FOV / N)) grad_term_x = -grad_term[1] grad_term_y = -grad_term[0] chisq_grad_epsilon[i_grad] = np.sum( dchisq_dIa * rF**2 * (EA_Gradient_x * grad_term_x + EA_Gradient_y * grad_term_y)) i_grad = i_grad + 1 #Imaginary part; remainder for s in range(1, (N + 1) / 2): for t in range(N): grad_term = so.Wrapped_Gradient( -wavelengthbar / FOV * self._sqrtQ[s][t] * 2.0 * np.sin(2.0 * np.pi / N * (ell_mat * s + m_mat * t)) / (FOV / N)) grad_term_x = -grad_term[1] grad_term_y = -grad_term[0] chisq_grad_epsilon[i_grad] = np.sum( dchisq_dIa * rF**2 * (EA_Gradient_x * grad_term_x + EA_Gradient_y * grad_term_y)) i_grad = i_grad + 1 # Gradient of the chi^2 regularization term for the epsilon screen chisq_epsilon_grad = self.alpha_phi_next * 2.0 * EpsilonList / ( (N * N - 1) / 2.0) # chain rule term for change of variables if self.transform_next == 'log': regterm *= imvec chisq_grad_im *= imvec return np.concatenate(((regterm + chisq_grad_im), (chisq_grad_epsilon + chisq_epsilon_grad)))