def optimize(self, tractor, alphas=None, damp=0, priors=True, scale_columns=True, shared_params=True, variance=False, just_variance=False, **nil): #logverb(tractor.getName() + ': Finding derivs...') #t0 = Time() allderivs = tractor.getDerivs() #tderivs = Time() - t0 #print(Time() - t0) #print('allderivs:', allderivs) # for d in allderivs: # for (p,im) in d: # print('patch mean', np.mean(p.patch)) #logverb('Finding optimal update direction...') #t0 = Time() X = self.getUpdateDirection(tractor, allderivs, damp=damp, priors=priors, scale_columns=scale_columns, shared_params=shared_params, variance=variance) #print('Update:', X) if X is None: # Failure return (0., None, 0.) if variance: if len(X) == 0: return 0, X, 0, None X, var = X if just_variance: return var #print(Time() - t0) #topt = Time() - t0 #print('X:', X) if len(X) == 0: return 0, X, 0. #logverb('X: len', len(X), '; non-zero entries:', np.count_nonzero(X)) logverb('Finding optimal step size...') #t0 = Time() (dlogprob, alpha) = self.tryUpdates(tractor, X, alphas=alphas) #tstep = Time() - t0 #logverb('Finished opt2.') #logverb(' alpha =', alpha) #logverb(' Tderiv', tderivs) #logverb(' Topt ', topt) #logverb(' Tstep ', tstep) # print('Stepped alpha=', alpha, 'for dlogprob', dlogprob) if variance: return dlogprob, X, alpha, var return dlogprob, X, alpha
def _optimize_forcedphot_core(self, tractor, result, umodels, imlist, mod0, scales, skyderivs, minFlux, nonneg=None, wantims0=None, wantims1=None, negfluxval=None, rois=None, priors=None, sky=None, justims0=None, subimgs=None, damp=None, alphas=None, Nsky=None, mindlnp=None, shared_params=None): # print(len(umodels), 'umodels, lengths', [len(x) for x in umodels]) if len(umodels) == 0: return Nsourceparams = len(umodels[0]) imgs = tractor.images t0 = Time() derivs = [[] for i in range(Nsourceparams)] for i, (tim, umods, scale) in enumerate(zip(imlist, umodels, scales)): for um, dd in zip(umods, derivs): if um is None: continue dd.append((um * scale, tim)) logverb('forced phot: derivs', Time() - t0) if sky: # Sky derivatives are part of the image derivatives, so go # first in the derivative list. derivs = skyderivs + derivs assert (len(derivs) == tractor.numberOfParams()) self._lsqr_forced_photom(tractor, result, derivs, mod0, imgs, umodels, rois, scales, priors, sky, minFlux, justims0, subimgs, damp, alphas, Nsky, mindlnp, shared_params)
def tryUpdates(self, tractor, X, alphas=None): if alphas is None: # 1/1024 to 1 in factors of 2, + sqrt(2.) + 2. alphas = np.append(2.**np.arange(-10, 1), [np.sqrt(2.), 2.]) pBefore = tractor.getLogProb() logverb(' log-prob before:', pBefore) pBest = pBefore alphaBest = None p0 = tractor.getParams() for alpha in alphas: logverb(' Stepping with alpha =', alpha) pa = [p + alpha * d for p, d in zip(p0, X)] tractor.setParams(pa) pAfter = tractor.getLogProb() logverb(' Log-prob after:', pAfter) logverb(' delta log-prob:', pAfter - pBefore) #print('Step', alpha, 'p', pAfter, 'dlnp', pAfter-pBefore) if not np.isfinite(pAfter): logmsg(' Got bad log-prob', pAfter) break if pAfter < (pBest - 1.): break if pAfter > pBest: alphaBest = alpha pBest = pAfter # if alphaBest is None or alphaBest == 0: # print "Warning: optimization is borking" # print "Parameter direction =",X # print "Parameters, step sizes, updates:" # for n,p,s,x in zip(tractor.getParamNames(), tractor.getParams(), tractor.getStepSizes(), X): # print n, '=', p, ' step', s, 'update', x if alphaBest is None: tractor.setParams(p0) return 0, 0. logverb(' Stepping by', alphaBest, 'for delta-logprob', pBest - pBefore) pa = [p + alphaBest * d for p, d in zip(p0, X)] tractor.setParams(pa) return pBest - pBefore, alphaBest
def _lsqr_forced_photom(self, tractor, result, derivs, mod0, imgs, umodels, rois, scales, priors, sky, minFlux, justims0, subimgs, damp, alphas, Nsky, mindlnp, shared_params): # About rois and derivs: we call # getUpdateDirection(derivs, ..., chiImages=[chis]) # And this uses the "img" objects in "derivs" to decide on the region # that is being optimized; the number of rows = total number of pixels. # We have to make sure that "chiImages" matches that size. # # We shift the unit-flux models (above, um.x0 -= x0) to be # relative to the ROI. # debugging images ims0 = None imsBest = None lnp0 = None chis0 = None quitNow = False # FIXME -- this should depend on the PhotoCal scalings! damp0 = 1e-3 damping = damp while True: # A flag to try again even if the lnprob got worse tryAgain = False p0 = tractor.getParams() if sky: p0sky = p0[:Nsky] p0 = p0[Nsky:] if lnp0 is None: #t0 = Time() lnp0, chis0, ims0 = self._lnp_for_update( tractor, mod0, imgs, umodels, None, None, p0, rois, scales, None, None, priors, sky, minFlux) #logverb('forced phot: initial lnp = ', # lnp0, 'took', Time() - t0) assert (np.isfinite(lnp0)) if justims0: result.lnp0 = lnp0 result.chis0 = chis0 result.ims0 = ims0 return # print('Starting opt loop with') # print(' p0', p0) # print(' lnp0', lnp0) # print(' chisqs', [(chi**2).sum() for chi in chis0]) # print('chis0:', chis0) # Ugly: getUpdateDirection calls self.getImages(), and # ASSUMES they are the same as the images referred-to in # the "derivs", to figure out which chi image goes with # which image. Temporarily set images = subimages if rois is not None: realims = tractor.images tractor.images = subimgs #logverb('forced phot: getting update with damp=', damping) #t0 = Time() X = self.getUpdateDirection(tractor, derivs, damp=damping, priors=priors, scale_columns=False, chiImages=chis0, shared_params=shared_params) #topt = Time() - t0 #logverb('forced phot: opt:', topt) #print('forced phot: update', X) if rois is not None: tractor.images = realims if len(X) == 0: print('Error getting update direction') break # tryUpdates(): if alphas is None: # 1/1024 to 1 in factors of 2, + sqrt(2.) + 2. alphas = np.append(2.**np.arange(-10, 1), [np.sqrt(2.), 2.]) if sky: # Split the sky-update parameters from the source parameters Xsky = X[:Nsky] X = X[Nsky:] else: p0sky = Xsky = None # Check whether the update produces all fluxes above the # minimums: if so we should be able to take the step with # alpha=1 and quit. if (minFlux is None) or np.all((p0 + X) >= minFlux): #print('Update produces non-negative fluxes; accepting with alpha=1') alphas = [1.] quitNow = True else: print('Some too-negative fluxes requested:') print('Fluxes:', p0) print('Update:', X) print('Total :', p0 + X) print('MinFlux:', minFlux) if damp == 0.0: damping = damp0 damp0 *= 10. print('Setting damping to', damping) if damp0 < 1e3: tryAgain = True lnpBest = lnp0 alphaBest = None chiBest = None for alpha in alphas: #t0 = Time() lnp, chis, ims = self._lnp_for_update(tractor, mod0, imgs, umodels, X, alpha, p0, rois, scales, p0sky, Xsky, priors, sky, minFlux) logverb('Forced phot: stepped with alpha', alpha, 'for lnp', lnp, ', dlnp', lnp - lnp0) #logverb('Took', Time() - t0) if lnp < (lnpBest - 1.): logverb('lnp', lnp, '< lnpBest-1', lnpBest - 1.) break if not np.isfinite(lnp): break if lnp > lnpBest: alphaBest = alpha lnpBest = lnp chiBest = chis imsBest = ims if alphaBest is not None: # Clamp fluxes up to zero if minFlux is not None: pa = [ max(minFlux, p + alphaBest * d) for p, d in zip(p0, X) ] else: pa = [p + alphaBest * d for p, d in zip(p0, X)] tractor.catalog.setParams(pa) if sky: tractor.images.setParams( [p + alpha * d for p, d in zip(p0sky, Xsky)]) dlogprob = lnpBest - lnp0 alpha = alphaBest lnp0 = lnpBest chis0 = chiBest # print('Accepting alpha =', alpha) # print('new lnp0', lnp0) # print('new chisqs', [(chi**2).sum() for chi in chis0]) # print('new params', self.getParams()) else: dlogprob = 0. alpha = 0. # ?? if sky: # Revert -- recall that we change params while probing in # lnpForUpdate() tractor.images.setParams(p0sky) #tstep = Time() - t0 #print('forced phot: line search:', tstep) #print('forced phot: alpha', alphaBest, 'for delta-lnprob', dlogprob) if dlogprob < mindlnp: if not tryAgain: break if quitNow: break result.ims0 = ims0 result.ims1 = imsBest
def getUpdateDirection(self, tractor, allderivs, damp=0., priors=True, scale_columns=True, scales_only=False, chiImages=None, variance=False, shared_params=True, get_A_matrix=False): # # Returns: numpy array containing update direction. # If *variance* is True, return (update,variance) # If *get_A_matrix* is True, returns the sparse matrix of derivatives. # If *scale_only* is True, return column scalings # In cases of an empty matrix, returns the list [] # # allderivs: [ # (param0:) [ (deriv, img), (deriv, img), ... ], # (param1:) [], # (param2:) [ (deriv, img), ], # ] # The "img"s may repeat # "deriv" are Patch objects. # Each position in the "allderivs" array corresponds to a # model parameter that we are optimizing # We want to minimize: # || chi + (d(chi)/d(params)) * dparams ||^2 # So b = chi # A = -d(chi)/d(params) # x = dparams # # chi = (data - model) / std = (data - model) * inverr # derivs = d(model)/d(param) # A matrix = -d(chi)/d(param) # = + (derivs) * inverr # Parameters to optimize go in the columns of matrix A # Pixels go in the rows. if shared_params: # Find shared parameters p0 = tractor.getParams() tractor.setParams(np.arange(len(p0))) p1 = tractor.getParams() tractor.setParams(p0) U, I = np.unique(p1, return_inverse=True) logverb(len(p0), 'params;', len(U), 'unique') paramindexmap = I #print('paramindexmap:', paramindexmap) #print('p1:', p1) # Build the sparse matrix of derivatives: sprows = [] spcols = [] spvals = [] # Keep track of row offsets for each image. imgoffs = {} nextrow = 0 for param in allderivs: for deriv, img in param: if img in imgoffs: continue imgoffs[img] = nextrow #print('Putting image', img.name, 'at row offset', nextrow) nextrow += img.numberOfPixels() Nrows = nextrow del nextrow Ncols = len(allderivs) # FIXME -- shared_params should share colscales! colscales = np.ones(len(allderivs)) for col, param in enumerate(allderivs): RR = [] VV = [] WW = [] for (deriv, img) in param: inverrs = img.getInvError() (H, W) = img.shape row0 = imgoffs[img] deriv.clipTo(W, H) pix = deriv.getPixelIndices(img) if len(pix) == 0: #print('This param does not influence this image!') continue assert (np.all(pix < img.numberOfPixels())) # (grab non-zero indices) dimg = deriv.getImage() nz = np.flatnonzero(dimg) #print(' source', j, 'derivative', p, 'has', len(nz), 'non-zero entries') if len(nz) == 0: continue rows = row0 + pix[nz] #print('Adding derivative', deriv.getName(), 'for image', img.name) vals = dimg.flat[nz] w = inverrs[deriv.getSlice(img)].flat[nz] assert (vals.shape == w.shape) # if not scales_only: RR.append(rows) VV.append(vals) WW.append(w) # massage, re-scale, and clean up matrix elements if len(VV) == 0: continue rows = np.hstack(RR) VV = np.hstack(VV) WW = np.hstack(WW) vals = VV * WW # shouldn't be necessary since we check len(nz)>0 above # if len(vals) == 0: # continue mx = np.max(np.abs(vals)) if mx == 0: logmsg('mx == 0:', len(np.flatnonzero(VV)), 'of', len(VV), 'non-zero derivatives,', len(np.flatnonzero(WW)), 'of', len(WW), 'non-zero weights;', len(np.flatnonzero(vals)), 'non-zero products') continue # MAGIC number: near-zero matrix elements -> 0 # 'mx' is the max value in this column. FACTOR = 1.e-10 I = (np.abs(vals) > (FACTOR * mx)) rows = rows[I] vals = vals[I] # L2 norm scale = np.sqrt(np.dot(vals, vals)) colscales[col] = scale if scales_only: continue sprows.append(rows) spcols.append(col) if scale_columns: if scale == 0.: spvals.append(vals) else: spvals.append(vals / scale) else: spvals.append(vals) if scales_only: return colscales b = None if priors: # We don't include the priors in the "colscales" # computation above, mostly because the priors are # returned as sparse additions to the matrix, and not # necessarily column-oriented the way the other params # are. It would be possible to make it work, but dstn is # not convinced it's worth the effort right now. X = tractor.getLogPriorDerivatives() if X is not None: rA, cA, vA, pb, mub = X sprows.extend([ri + Nrows for ri in rA]) spcols.extend(cA) spvals.extend([vi / colscales[ci] for vi, ci in zip(vA, cA)]) oldnrows = Nrows nr = listmax(rA, -1) + 1 Nrows += nr logverb('Nrows was %i, added %i rows of priors => %i' % (oldnrows, nr, Nrows)) # if len(cA) == 0: # Ncols = 0 # else: # Ncols = 1 + max(cA) b = np.zeros(Nrows) b[oldnrows:] = np.hstack(pb) if len(spcols) == 0: logverb("len(spcols) == 0") return [] # 'spcols' has one integer per 'sprows' block. # below we hstack the rows, but before doing that, remember how # many rows are in each chunk. spcols = np.array(spcols) nrowspercol = np.array([len(x) for x in sprows]) if shared_params: # Apply shared parameter map #print('Before applying shared parameter map:') #print('spcols:', len(spcols), 'elements') #print(' ', len(set(spcols)), 'unique') spcols = paramindexmap[spcols] # print('After:') #print('spcols:', len(spcols), 'elements') #print(' ', len(set(spcols)), 'unique') Ncols = np.max(spcols) + 1 logverb('Set Ncols=', Ncols) # b = chi # # FIXME -- we could be much smarter here about computing # just the regions we need! # if b is None: b = np.zeros(Nrows) chimap = {} if chiImages is not None: for img, chi in zip(tractor.getImages(), chiImages): chimap[img] = chi # FIXME -- could compute just chi ROIs here. # iterating this way avoids setting the elements more than once for img, row0 in imgoffs.items(): chi = chimap.get(img, None) if chi is None: #print('computing chi image') chi = tractor.getChiImage(img=img) chi = chi.ravel() NP = len(chi) # we haven't touched these pix before assert (np.all(b[row0:row0 + NP] == 0)) assert (np.all(np.isfinite(chi))) #print('Setting [%i:%i) from chi img' % (row0, row0+NP)) b[row0:row0 + NP] = chi # Zero out unused rows -- FIXME, is this useful?? # print('Nrows', Nrows, 'vs len(urows)', len(urows)) # bnz = np.zeros(Nrows) # bnz[urows] = b[urows] # print('b', len(b), 'vs bnz', len(bnz)) # b = bnz assert (np.all(np.isfinite(b))) from scipy.sparse import csr_matrix from scipy.sparse.linalg import lsqr spvals = np.hstack(spvals) if not np.all(np.isfinite(spvals)): print('Warning: infinite derivatives; bailing out') return None assert (np.all(np.isfinite(spvals))) sprows = np.hstack(sprows) # hogg's lovin' hstack *again* here assert (len(sprows) == len(spvals)) # For LSQR, expand 'spcols' to be the same length as 'sprows'. cc = np.empty(len(sprows)) i = 0 for c, n in zip(spcols, nrowspercol): cc[i:i + n] = c i += n spcols = cc assert (i == len(sprows)) assert (len(sprows) == len(spcols)) logverb(' Number of sparse matrix elements:', len(sprows)) urows = np.unique(sprows) ucols = np.unique(spcols) logverb(' Unique rows (pixels):', len(urows)) logverb(' Unique columns (params):', len(ucols)) if len(urows) == 0 or len(ucols) == 0: return [] logverb(' Max row:', urows[-1]) logverb(' Max column:', ucols[-1]) logverb(' Sparsity factor (possible elements / filled elements):', float(len(urows) * len(ucols)) / float(len(sprows))) # FIXME -- does it make LSQR faster if we remap the row and column # indices so that no rows/cols are empty? # FIXME -- we could probably construct the CSC matrix ourselves! # Build sparse matrix #A = csc_matrix((spvals, (sprows, spcols)), shape=(Nrows, Ncols)) A = csr_matrix((spvals, (sprows, spcols)), shape=(Nrows, Ncols)) if get_A_matrix: return A lsqropts = dict(show=isverbose(), damp=damp) if variance: lsqropts.update(calc_var=True) # Run lsqr() logverb('LSQR: %i cols (%i unique), %i elements' % (Ncols, len(ucols), len(spvals) - 1)) # print('A matrix:') # print(A.todense()) # print('vector b:') # print(b) bail = False try: # lsqr can trigger floating-point errors oldsettings = np.seterr(all='print') (X, istop, niters, r1norm, r2norm, anorm, acond, arnorm, xnorm, var) = lsqr(A, b, **lsqropts) except ZeroDivisionError: print('ZeroDivisionError caught. Returning zero.') bail = True # finally: np.seterr(**oldsettings) del A del b if bail: if shared_params: return np.zeros(len(paramindexmap)) return np.zeros(len(allderivs)) # print('LSQR results:') # print(' istop =', istop) # print(' niters =', niters) # print(' r1norm =', r1norm) # print(' r2norm =', r2norm) # print(' anorm =', anorm) # print(' acord =', acond) # print(' arnorm =', arnorm) # print(' xnorm =', xnorm) # print(' var =', var) logverb('scaled X=', X) X = np.array(X) if shared_params: # Unapply shared parameter map -- result is duplicated # result elements. # logverb('shared_params: before, X len', len(X), 'with', # np.count_nonzero(X), 'non-zero entries') # logverb('paramindexmap: len', len(paramindexmap), # 'range', paramindexmap.min(), paramindexmap.max()) X = X[paramindexmap] # logverb('shared_params: after, X len', len(X), 'with', # np.count_nonzero(X), 'non-zero entries') if scale_columns: X[colscales > 0] /= colscales[colscales > 0] logverb(' X=', X) if variance: if shared_params: # Unapply shared parameter map. var = var[paramindexmap] if scale_columns: var[colscales > 0] /= colscales[colscales > 0]**2 return X, var return X
def _ceres_forced_photom(self, tractor, result, umodels, imlist, mods0, scales, skyderivs, minFlux, nonneg=False, wantims0=True, wantims1=True, negfluxval=None, verbose=False, **kwargs): ''' negfluxval: when 'nonneg' is set, the flux value to give sources that went negative in an unconstrained fit. ''' from tractor.ceres import ceres_forced_phot t0 = Time() blocks = [] blockstart = {} usedParamMap = {} nextparam = 0 # umodels[ imagei, srci ] = Patch Nsky = 0 Z = [] if skyderivs is not None: # skyderivs = [ (param0:)[ (deriv,img), ], (param1:)[ (deriv,img), ], ...] # Reorg them to be in img-major order skymods = [[] for im in imlist] for skyd in skyderivs: for (deriv, img) in skyd: imi = imlist.index(img) skymods[imi].append(deriv) for mods, im, mod0 in zip(skymods, imlist, mods0): Z.append((mods, im, 1., mod0, Nsky)) Nsky += len(mods) Z.extend( zip(umodels, imlist, scales, mods0, np.zeros(len(imlist), int) + Nsky)) sky = (skyderivs is not None) for zi, (umods, img, scale, mod0, paramoffset) in enumerate(Z): H, W = img.shape if img in blockstart: (b0, nbw, nbh) = blockstart[img] else: # Dice up the image nbw = int(np.ceil(W / float(self.BW))) nbh = int(np.ceil(H / float(self.BH))) b0 = len(blocks) blockstart[img] = (b0, nbw, nbh) for iy in range(nbh): for ix in range(nbw): x0 = ix * self.BW y0 = iy * self.BH slc = (slice(y0, min(y0 + self.BH, H)), slice(x0, min(x0 + self.BW, W))) data = (x0, y0, img.getImage()[slc].astype(self.ceresType), mod0[slc].astype(self.ceresType), img.getInvError()[slc].astype(self.ceresType)) blocks.append((data, [])) for modi, umod in enumerate(umods): if umod is None: continue # DEBUG if len(umod.shape) != 2: print('zi', zi) print('modi', modi) print('umod', umod) umod.clipTo(W, H) umod.trimToNonZero() if umod.patch is None: continue # Dice up the model ph, pw = umod.shape bx0 = np.clip(int(np.floor(umod.x0 / float(self.BW))), 0, nbw - 1) bx1 = np.clip(int(np.ceil((umod.x0 + pw) / float(self.BW))), 0, nbw - 1) by0 = np.clip(int(np.floor(umod.y0 / float(self.BH))), 0, nbh - 1) by1 = np.clip(int(np.ceil((umod.y0 + ph) / float(self.BH))), 0, nbh - 1) parami = paramoffset + modi if parami in usedParamMap: ceresparam = usedParamMap[parami] else: usedParamMap[parami] = nextparam ceresparam = nextparam nextparam += 1 cmod = (umod.patch * scale).astype(self.ceresType) for by in range(by0, by1 + 1): for bx in range(bx0, bx1 + 1): bi = by * nbw + bx # if type(umod.x0) != int or type(umod.y0) != int: # print('umod:', umod.x0, umod.y0, type(umod.x0), type(umod.y0)) # print('umod:', umod) dd = (ceresparam, int(umod.x0), int(umod.y0), cmod) blocks[b0 + bi][1].append(dd) logverb('forced phot: dicing up', Time() - t0) if wantims0: t0 = Time() params = tractor.getParams() result.ims0 = self._getims(params, imlist, umodels, mods0, scales, sky, minFlux, None) logverb('forced phot: ims0', Time() - t0) t0 = Time() fluxes = np.zeros(len(usedParamMap)) logverb('Ceres forced phot:') logverb(len(blocks), ('image blocks (%ix%i), %i params' % (self.BW, self.BH, len(fluxes)))) if len(blocks) == 0 or len(fluxes) == 0: logverb('Nothing to do!') return # init fluxes passed to ceres p0 = tractor.getParams() for i, k in usedParamMap.items(): fluxes[k] = p0[i] iverbose = 1 if verbose else 0 nonneg = int(nonneg) ithreads = 0 if self.threads is not None: ithreads = int(self.threads) if nonneg: # Initial run with nonneg=False, to get in the ballpark x = ceres_forced_phot(blocks, fluxes, 0, iverbose, ithreads) assert (x == 0) logverb('forced phot: ceres initial run', Time() - t0) t0 = Time() if negfluxval is not None: fluxes = np.maximum(fluxes, negfluxval) x = ceres_forced_phot(blocks, fluxes, nonneg, iverbose, ithreads) #print('Ceres forced phot:', x) logverb('forced phot: ceres', Time() - t0) t0 = Time() params = np.zeros(len(p0)) for i, k in usedParamMap.items(): params[i] = fluxes[k] tractor.setParams(params) logverb('forced phot: unmapping params:', Time() - t0) if wantims1: t0 = Time() result.ims1 = self._getims(params, imlist, umodels, mods0, scales, sky, minFlux, None) logverb('forced phot: ims1:', Time() - t0) return x
def forced_photometry(self, tractor, alphas=None, damp=0, priors=False, minsb=0., mindlnp=1., rois=None, sky=False, minFlux=None, fitstats=False, fitstat_extras=None, justims0=False, variance=False, skyvariance=False, shared_params=True, nonneg=False, nilcounts=-1e30, wantims=True, negfluxval=None, **kwargs): from tractor.basics import LinearPhotoCal, ShiftedWcs result = OptResult() assert (not priors) scales = [] imgs = tractor.getImages() for img in imgs: assert (isinstance(img.getPhotoCal(), LinearPhotoCal)) scales.append(img.getPhotoCal().getScale()) if rois is not None: assert (len(rois) == len(imgs)) # HACK -- if sky=True, assume we are fitting the sky in ALL images. # We could ask which ones are thawed... if sky: for img in imgs: # FIXME -- would be nice to allow multi-param linear sky models assert (img.getSky().numberOfParams() == 1) Nsourceparams = tractor.catalog.numberOfParams() srcs = list(tractor.catalog.getThawedSources()) # Render unit-flux models for each source. t0 = Time() (umodels, umodtosource, umodsforsource) = self._get_umodels(tractor, srcs, imgs, minsb, rois) for umods in umodels: assert (len(umods) == Nsourceparams) tmods = Time() - t0 logverb('forced phot: getting unit-flux models:', tmods) subimgs = [] if rois is not None: for i, img in enumerate(imgs): from tractor.image import Image roi = rois[i] y0 = roi[0].start x0 = roi[1].start y1 = roi[0].stop x1 = roi[1].stop subimg = img.subimage(x0, x1, y0, y1) subimgs.append(subimg) imlist = subimgs else: imlist = imgs t0 = Time() fsrcs = list(tractor.catalog.getFrozenSources()) mod0 = [] for img in imlist: # "sky = not sky": I'm not just being contrary :) # If we're fitting sky, we'll do a setParams() and get the # sky models to render themselves when evaluating lnProbs, # rather than pre-computing the nominal value here and # then computing derivatives. mod0.append( tractor.getModelImage(img, fsrcs, minsb=minsb, sky=not sky)) tmod = Time() - t0 logverb('forced phot: getting frozen-source model:', tmod) skyderivs = None if sky: t0 = Time() # build the derivative list as required by getUpdateDirection: # (param0) -> [ (deriv, img), (deriv, img), ... ], ... ], skyderivs = [] for img in imlist: dskys = img.getSky().getParamDerivatives(tractor, img, None) for dsky in dskys: skyderivs.append([(dsky, img)]) Nsky = len(skyderivs) assert (Nsky == tractor.images.numberOfParams()) assert (Nsky + Nsourceparams == tractor.numberOfParams()) logverb('forced phot: sky derivs', Time() - t0) else: Nsky = 0 wantims0 = wantims1 = wantims if fitstats: wantims1 = True self._optimize_forcedphot_core(tractor, result, umodels, imlist, mod0, scales, skyderivs, minFlux, nonneg=nonneg, wantims0=wantims0, wantims1=wantims1, negfluxval=negfluxval, rois=rois, priors=priors, sky=sky, justims0=justims0, subimgs=subimgs, damp=damp, alphas=alphas, Nsky=Nsky, mindlnp=mindlnp, shared_params=shared_params, **kwargs) if variance: # Inverse variance t0 = Time() result.IV = self._get_iv(sky, skyvariance, Nsky, skyderivs, Nsourceparams, imlist, umodels, scales) logverb('forced phot: variance:', Time() - t0) imsBest = getattr(result, 'ims1', None) if fitstats and imsBest is None: print('Warning: fit stats not computed because imsBest is None') result.fitstats = None elif fitstats: t0 = Time() result.fitstats = self._get_fitstats(tractor.catalog, imsBest, srcs, imlist, umodsforsource, umodels, scales, nilcounts, extras=fitstat_extras) logverb('forced phot: fit stats:', Time() - t0) return result
def setParams(self, p): logverb('CeresTractorAdapter: setParams:', self.offset + self.scale * p) return self.tractor.setParams(self.offset + self.scale * p)
def getChiImage(self, i): logverb('CeresTractorAdapter: getChiImage(%i)' % i) return self.tractor.getChiImage(i)