LSST Applications g0b6bd0c080+a72a5dd7e6,g1182afd7b4+2a019aa3bb,g17e5ecfddb+2b8207f7de,g1d67935e3f+06cf436103,g38293774b4+ac198e9f13,g396055baef+6a2097e274,g3b44f30a73+6611e0205b,g480783c3b1+98f8679e14,g48ccf36440+89c08d0516,g4b93dc025c+98f8679e14,g5c4744a4d9+a302e8c7f0,g613e996a0d+e1c447f2e0,g6c8d09e9e7+25247a063c,g7271f0639c+98f8679e14,g7a9cd813b8+124095ede6,g9d27549199+a302e8c7f0,ga1cf026fa3+ac198e9f13,ga32aa97882+7403ac30ac,ga786bb30fb+7a139211af,gaa63f70f4e+9994eb9896,gabf319e997+ade567573c,gba47b54d5d+94dc90c3ea,gbec6a3398f+06cf436103,gc6308e37c7+07dd123edb,gc655b1545f+ade567573c,gcc9029db3c+ab229f5caf,gd01420fc67+06cf436103,gd877ba84e5+06cf436103,gdb4cecd868+6f279b5b48,ge2d134c3d5+cc4dbb2e3f,ge448b5faa6+86d1ceac1d,gecc7e12556+98f8679e14,gf3ee170dca+25247a063c,gf4ac96e456+ade567573c,gf9f5ea5b4d+ac198e9f13,gff490e6085+8c2580be5c,w.2022.27
LSST Data Management Base Package
optimizerDisplay.py
Go to the documentation of this file.
2# LSST Data Management System
3# Copyright 2008-2013 LSST Corporation.
4#
5# This product includes software developed by the
6# LSST Project (http://www.lsst.org/).
7#
8# This program is free software: you can redistribute it and/or modify
9# it under the terms of the GNU General Public License as published by
10# the Free Software Foundation, either version 3 of the License, or
11# (at your option) any later version.
12#
13# This program is distributed in the hope that it will be useful,
14# but WITHOUT ANY WARRANTY; without even the implied warranty of
15# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16# GNU General Public License for more details.
17#
18# You should have received a copy of the LSST License Statement and
19# the GNU General Public License along with this program. If not,
20# see <http://www.lsstcorp.org/LegalNotices/>.
21#
22
23import numpy
24import matplotlib
25import matplotlib.colors
26from mpl_toolkits.axes_grid1 import make_axes_locatable
27
28from .densityPlot import hide_xticklabels, hide_yticklabels
29from .. import modelfitLib
30
31__all__ = ("OptimizerDisplay", )
32
33
35
36 def __init__(self, parent, sample):
37 self.parentparent = parent
38 self.samplesample = sample
39 # scale unit grid by trust radius
40 self.gridgrid = parent.unitGrid * sample.get(parent.recorder.trust)
41 # offset grid to center it on the current parameter point
42 self.gridgrid += sample.get(parent.recorder.parameters).reshape((1,)*parent.ndim + (parent.ndim,))
43 self._objectiveValues_objectiveValues = None
44 self._objectiveModel_objectiveModel = None
45 self.rejectedrejected = []
46
47 def __getattr__(self, name):
48 # look for keys on the recorder and lookup fields for unknown attributes
49 return self.samplesample.get(getattr(self.parentparent.recorder, name))
50
51 @property
52 def objectiveValues(self):
53 if self._objectiveValues_objectiveValues is None:
54 self._objectiveValues_objectiveValues = numpy.zeros(self.gridgrid.shape[:-1], dtype=float)
55 self.parentparent.objective.fillObjectiveValueGrid(self.gridgrid.reshape(-1, self.parentparent.ndim),
56 self._objectiveValues_objectiveValues.reshape(-1))
57 good = numpy.isfinite(self._objectiveValues_objectiveValues)
58 self._objectiveValues_objectiveValues[numpy.logical_not(good)] = self._objectiveValues_objectiveValues[good].max()
59 return self._objectiveValues_objectiveValues
60
61 @property
62 def objectiveModel(self):
63 if self._objectiveModel_objectiveModel is None:
64 self._objectiveModel_objectiveModel = numpy.zeros(self.gridgrid.shape[:-1], dtype=float)
65 self.parentparent.recorder.fillObjectiveModelGrid(self.samplesample,
66 self.gridgrid.reshape(-1, self.parentparent.ndim),
67 self._objectiveModel_objectiveModel.reshape(-1))
68 return self._objectiveModel_objectiveModel
69
70
72
73 def __init__(self, history, model, objective, steps=11):
74 self.recorderrecorder = modelfitLib.OptimizerHistoryRecorder(history.schema)
75 # len(dimensions) == N in comments below
76 self.dimensionsdimensions = list(model.getNonlinearNames()) + list(model.getAmplitudeNames())
77 self.ndimndim = len(self.dimensionsdimensions)
78 self.tracktrack = []
79 self.objectiveobjective = objective
80 # This creates a array with shape [steps, ..., steps, N] (a total of N+1 array dimensions):
81 # this is an N-dimensional grid, with the last dimension of the grid array giving the coordinates
82 # of each grid point.
83 # We slice mgrid to generate the basic grid, which is [N, steps, ..., steps]
84 mgridArgs = (slice(-1.0, 1.0, steps*1j),) * self.ndimndim
85 # We'll index the result of mgrid with these args to make first dimension last
86 transposeArgs = tuple(list(range(1, self.ndimndim+1)) + [0])
87 self.unitGridunitGrid = numpy.mgrid[mgridArgs].transpose(transposeArgs).copy()
88 current = None
89 for sample in history:
90 if sample.get(self.recorderrecorder.state) & modelfitLib.Optimizer.STATUS_STEP_REJECTED:
91 assert current is not None
92 current.rejected.append(sample)
93 continue
94 current = OptimizerIterationDisplay(self, sample)
95 self.tracktrack.append(current)
96
97 def plot(self, xDim, yDim, n=0):
98 return OptimizerDisplayFigure(self, xDim=xDim, yDim=yDim, n=n)
99
100
102
103 def __init__(self, parent, xDim, yDim, n=0):
104 self.parentparent = parent
105 self.xDimxDim = xDim
106 self.yDimyDim = yDim
107 self.jj = self.parentparent.dimensions.index(self.xDimxDim)
108 self.ii = self.parentparent.dimensions.index(self.yDimyDim)
109 self.yKeyyKey = self.parentparent.recorder.parameters[self.ii]
110 self.xKeyxKey = self.parentparent.recorder.parameters[self.jj]
111 self.zKeyzKey = self.parentparent.recorder.objective
112 # grid slice indices corresponding to the dimensions we're plotting
113 self.slice2dslice2d = [s//2 for s in self.parentparent.unitGrid.shape[:-1]]
114 self.slice2dslice2d[self.ii] = slice(None)
115 self.slice2dslice2d[self.jj] = slice(None)
116 self.slice2dslice2d = tuple(self.slice2dslice2d)
117 self.sliceXsliceX = [s//2 for s in self.parentparent.unitGrid.shape[:-1]]
118 self.sliceXsliceX[self.jj] = slice(None)
119 self.sliceXsliceX = tuple(self.sliceXsliceX)
120 self.sliceYsliceY = [s//2 for s in self.parentparent.unitGrid.shape[:-1]]
121 self.sliceYsliceY[self.ii] = slice(None)
122 self.sliceYsliceY = tuple(self.sliceYsliceY)
123 self.tracktrack = dict(
124 x=numpy.array([iteration.sample.get(self.xKeyxKey) for iteration in self.parentparent.track]),
125 y=numpy.array([iteration.sample.get(self.yKeyyKey) for iteration in self.parentparent.track]),
126 z=numpy.array([iteration.sample.get(self.zKeyzKey) for iteration in self.parentparent.track]),
127 )
128 self.nn = n
129 self.figurefigure = matplotlib.pyplot.figure(f"{xDim} vs {yDim}", figsize=(16, 8))
130 self.figurefigure.subplots_adjust(left=0.025, right=0.975, bottom=0.08, top=0.95, wspace=0.12)
131 self.axes3daxes3d = self.figurefigure.add_subplot(1, 2, 1, projection='3d')
132 self.axes3daxes3d.autoscale(False)
133 self.axes3daxes3d.set_xlabel(self.xDimxDim)
134 self.axes3daxes3d.set_ylabel(self.yDimyDim)
135 self.axes2daxes2d = self.figurefigure.add_subplot(1, 2, 2)
136 self.axes2daxes2d.set_xlabel(self.xDimxDim)
137 self.axes2daxes2d.set_ylabel(self.yDimyDim)
138 self.axes2daxes2d.autoscale(False)
139 divider = make_axes_locatable(self.axes2daxes2d)
140 self.axesXaxesX = divider.append_axes("top", 1.5, pad=0.1, sharex=self.axes2daxes2d)
141 self.axesXaxesX.autoscale(False, axis='x')
142 hide_xticklabels(self.axesXaxesX)
143 self.axesYaxesY = divider.append_axes("right", 1.5, pad=0.1, sharey=self.axes2daxes2d)
144 self.axesYaxesY.autoscale(False, axis='y')
145 hide_yticklabels(self.axesYaxesY)
146 self.artistsartists = []
147 self.guessExtentguessExtent()
148 self.plotTrackplotTrack()
149 self.plotRejectedplotRejected()
150 self.plotSurfacesplotSurfaces()
151
152 @property
153 def xlim(self):
154 return self._extent_extent[:2]
155
156 @property
157 def ylim(self):
158 return self._extent_extent[2:4]
159
160 @property
161 def zlim(self):
162 return self._extent_extent[4:]
163
164 def guessExtent(self):
165 current = self.parentparent.track[self.nn]
166 x = current.sample.get(self.xKeyxKey)
167 y = current.sample.get(self.yKeyyKey)
168 zMin1 = current.objectiveValues[self.slice2dslice2d].min()
169 zMax1 = current.objectiveValues[self.slice2dslice2d].max()
170 zMin2 = current.objectiveModel[self.slice2dslice2d].min()
171 zMax2 = current.objectiveModel[self.slice2dslice2d].max()
172 self.setExtentsetExtent(x0=x - current.trust, x1=x + current.trust,
173 y0=y - current.trust, y1=y + current.trust,
174 z0=min(zMin1, zMin2), z1=max(zMax1, zMax2), lock=False)
175
176 def setExtent(self, x0=None, x1=None, y0=None, y1=None, z0=None, z1=None, lock=True):
177 if x0 is None:
178 x0 = self._extent_extent[0]
179 if x1 is None:
180 x1 = self._extent_extent[1]
181 if y0 is None:
182 y0 = self._extent_extent[2]
183 if y1 is None:
184 y1 = self._extent_extent[3]
185 if z0 is None:
186 z0 = self._extent_extent[4]
187 if z1 is None:
188 z1 = self._extent_extent[5]
189 self._extent_extent = (x0, x1, y0, y1, z0, z1)
190 self._lock_lock = lock
191 self.axes3daxes3d.set_xlim(*self.xlimxlim)
192 self.axes3daxes3d.set_ylim(*self.ylimylim)
193 self.axes3daxes3d.set_zlim(*self.zlimzlim)
194 self.axes2daxes2d.set_xlim(*self.xlimxlim)
195 self.axes2daxes2d.set_ylim(*self.ylimylim)
196 self.axesXaxesX.set_ylim(*self.zlimzlim)
197 self.axesYaxesY.set_xlim(*self.zlimzlim)
198
199 def _clipZ(self, x, y, z):
200 # clipping is currently disabled; more trouble than it's worth
201 if False:
202 mask = numpy.logical_or.reduce([x < self.xlimxlim[0], x > self.xlimxlim[1],
203 y < self.ylimylim[0], y > self.ylimylim[1],
204 z < self.zlimzlim[0], z > self.zlimzlim[1]],
205 axis=0)
206
207 z[mask] = numpy.nan
208 return numpy.logical_not(mask).astype(int).sum() > 4
209 return True
210
211 def _contour(self, axes, *args, **kwds):
212 self.artistsartists.extend(axes.contour(*args, **kwds).collections)
213
214 def plotTrack(self):
215 kwds = dict(markeredgewidth=0, markerfacecolor='g', color='g', marker='o')
216 self.axes3daxes3d.plot(self.tracktrack['x'], self.tracktrack['y'], self.tracktrack['z'], **kwds)
217 self.axes2daxes2d.plot(self.tracktrack['x'], self.tracktrack['y'], **kwds)
218 self.axesXaxesX.plot(self.tracktrack['x'], self.tracktrack['z'], **kwds)
219 self.axesYaxesY.plot(self.tracktrack['z'], self.tracktrack['y'], **kwds)
220
221 def plotRejected(self):
222 kwds = dict(markeredgewidth=0, markerfacecolor='r', color='r', marker='v')
223 current = self.parentparent.track[self.nn]
224 cx = current.sample.get(self.xKeyxKey)
225 cy = current.sample.get(self.yKeyyKey)
226 cz = current.sample.get(self.zKeyzKey)
227 for r in current.rejected:
228 x = [cx, r.get(self.xKeyxKey)]
229 y = [cy, r.get(self.yKeyyKey)]
230 z = [cz, r.get(self.zKeyzKey)]
231 self.artistsartists.extend(self.axes3daxes3d.plot(x, y, z, **kwds))
232 self.artistsartists.extend(self.axes2daxes2d.plot(x, y, **kwds))
233 self.artistsartists.extend(self.axesXaxesX.plot(x, z, **kwds))
234 self.artistsartists.extend(self.axesYaxesY.plot(z, y, **kwds))
235
236 def plotSurfaces(self):
237 current = self.parentparent.track[self.nn]
238
239 # Start with 2-d and 3-d surfaces
240 x = current.grid[self.slice2dslice2d + (self.jj,)]
241 y = current.grid[self.slice2dslice2d + (self.ii,)]
242 z1 = current.objectiveValues[self.slice2dslice2d].copy()
243 z2 = current.objectiveModel[self.slice2dslice2d].copy()
244 norm = matplotlib.colors.Normalize(vmin=self.zlimzlim[0], vmax=self.zlimzlim[1])
245
246 self._contour_contour(self.axes2daxes2d, x, y, z1, cmap=matplotlib.cm.spring, norm=norm)
247 self._contour_contour(self.axes2daxes2d, x, y, z2, cmap=matplotlib.cm.winter, norm=norm)
248
249 # matplotlib doesn't do clipping in 3d, so we'll do that manually
250 if self._clipZ_clipZ(x, y, z1):
251 self._contour_contour(self.axes3daxes3d, x, y, z1, cmap=matplotlib.cm.spring, norm=norm)
252 self.artistsartists.append(self.axes3daxes3d.plot_surface(x, y, z1, rstride=1, cstride=1,
253 cmap=matplotlib.cm.spring, norm=norm,
254 linewidth=0, antialiased=1, alpha=0.5))
255 if self._clipZ_clipZ(x, y, z2):
256 self._contour_contour(self.axes3daxes3d, x, y, z2, cmap=matplotlib.cm.winter, norm=norm)
257 self.artistsartists.append(self.axes3daxes3d.plot_surface(x, y, z2, rstride=1, cstride=1,
258 cmap=matplotlib.cm.winter, norm=norm,
259 linewidth=0, antialiased=1, alpha=0.5))
260
261 # Now the 1-d surfaces
262 self.artistsartists.extend(self.axesXaxesX.plot(current.grid[self.sliceXsliceX + (self.jj,)],
263 current.objectiveValues[self.sliceXsliceX], 'm-'))
264 self.artistsartists.extend(self.axesXaxesX.plot(current.grid[self.sliceXsliceX + (self.jj,)],
265 current.objectiveModel[self.sliceXsliceX], 'c-'))
266 self.artistsartists.extend(self.axesYaxesY.plot(current.objectiveValues[self.sliceYsliceY],
267 current.grid[self.sliceYsliceY + (self.ii,)], 'm-'))
268 self.artistsartists.extend(self.axesYaxesY.plot(current.objectiveModel[self.sliceYsliceY],
269 current.grid[self.sliceYsliceY + (self.ii,)], 'c-'))
270
271 def move(self, n):
272 self.nn = n
273 if not self._lock_lock:
274 self.guessExtentguessExtent()
275 for artist in self.artistsartists:
276 try:
277 artist.remove()
278 except TypeError:
279 # sometimes matplotlib throws an exception even though everything worked fine
280 pass
281 self.artistsartists = []
282 self.plotSurfacesplotSurfaces()
283 self.plotRejectedplotRejected()
284 self.figurefigure.canvas.draw()
int min
int max
def setExtent(self, x0=None, x1=None, y0=None, y1=None, z0=None, z1=None, lock=True)
def __init__(self, history, model, objective, steps=11)
daf::base::PropertyList * list
Definition: fits.cc:913
std::shared_ptr< FrameSet > append(FrameSet const &first, FrameSet const &second)
Construct a FrameSet that performs two transformations in series.
Definition: functional.cc:33
def plot(mag, width, centers, clusterId, marker="o", markersize=2, markeredgewidth=0, ltype='-', magType="model", clear=True)