LSSTApplications  18.0.0+106,18.0.0+50,19.0.0,19.0.0+1,19.0.0+10,19.0.0+11,19.0.0+13,19.0.0+17,19.0.0+2,19.0.0-1-g20d9b18+6,19.0.0-1-g425ff20,19.0.0-1-g5549ca4,19.0.0-1-g580fafe+6,19.0.0-1-g6fe20d0+1,19.0.0-1-g7011481+9,19.0.0-1-g8c57eb9+6,19.0.0-1-gb5175dc+11,19.0.0-1-gdc0e4a7+9,19.0.0-1-ge272bc4+6,19.0.0-1-ge3aa853,19.0.0-10-g448f008b,19.0.0-12-g6990b2c,19.0.0-2-g0d9f9cd+11,19.0.0-2-g3d9e4fb2+11,19.0.0-2-g5037de4,19.0.0-2-gb96a1c4+3,19.0.0-2-gd955cfd+15,19.0.0-3-g2d13df8,19.0.0-3-g6f3c7dc,19.0.0-4-g725f80e+11,19.0.0-4-ga671dab3b+1,19.0.0-4-gad373c5+3,19.0.0-5-ga2acb9c+2,19.0.0-5-gfe96e6c+2,w.2020.01
LSSTDataManagementBasePackage
optimizerDisplay.py
Go to the documentation of this file.
1 #
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 
23 import numpy
24 import matplotlib
25 import matplotlib.colors
26 from mpl_toolkits.axes_grid1 import make_axes_locatable
27 
28 from .densityPlot import hide_xticklabels, hide_yticklabels
29 from .. import modelfitLib
30 
31 __all__ = ("OptimizerDisplay", )
32 
33 
35 
36  def __init__(self, parent, sample):
37  self.parent = parent
38  self.sample = sample
39  # scale unit grid by trust radius
40  self.grid = parent.unitGrid * sample.get(parent.recorder.trust)
41  # offset grid to center it on the current parameter point
42  self.grid += sample.get(parent.recorder.parameters).reshape((1,)*parent.ndim + (parent.ndim,))
43  self._objectiveValues = None
44  self._objectiveModel = None
45  self.rejected = []
46 
47  def __getattr__(self, name):
48  # look for keys on the recorder and lookup fields for unknown attributes
49  return self.sample.get(getattr(self.parent.recorder, name))
50 
51  @property
52  def objectiveValues(self):
53  if self._objectiveValues is None:
54  self._objectiveValues = numpy.zeros(self.grid.shape[:-1], dtype=float)
55  self.parent.objective.fillObjectiveValueGrid(self.grid.reshape(-1, self.parent.ndim),
56  self._objectiveValues.reshape(-1))
57  good = numpy.isfinite(self._objectiveValues)
58  self._objectiveValues[numpy.logical_not(good)] = self._objectiveValues[good].max()
59  return self._objectiveValues
60 
61  @property
62  def objectiveModel(self):
63  if self._objectiveModel is None:
64  self._objectiveModel = numpy.zeros(self.grid.shape[:-1], dtype=float)
65  self.parent.recorder.fillObjectiveModelGrid(self.sample,
66  self.grid.reshape(-1, self.parent.ndim),
67  self._objectiveModel.reshape(-1))
68  return self._objectiveModel
69 
70 
72 
73  def __init__(self, history, model, objective, steps=11):
74  self.recorder = modelfitLib.OptimizerHistoryRecorder(history.schema)
75  # len(dimensions) == N in comments below
76  self.dimensions = list(model.getNonlinearNames()) + list(model.getAmplitudeNames())
77  self.ndim = len(self.dimensions)
78  self.track = []
79  self.objective = 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.ndim
85  # We'll index the result of mgrid with these args to make first dimension last
86  transposeArgs = tuple(list(range(1, self.ndim+1)) + [0])
87  self.unitGrid = numpy.mgrid[mgridArgs].transpose(transposeArgs).copy()
88  current = None
89  for sample in history:
90  if sample.get(self.recorder.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.track.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.parent = parent
105  self.xDim = xDim
106  self.yDim = yDim
107  self.j = self.parent.dimensions.index(self.xDim)
108  self.i = self.parent.dimensions.index(self.yDim)
109  self.yKey = self.parent.recorder.parameters[self.i]
110  self.xKey = self.parent.recorder.parameters[self.j]
111  self.zKey = self.parent.recorder.objective
112  # grid slice indices corresponding to the dimensions we're plotting
113  self.slice2d = [s//2 for s in self.parent.unitGrid.shape[:-1]]
114  self.slice2d[self.i] = slice(None)
115  self.slice2d[self.j] = slice(None)
116  self.slice2d = tuple(self.slice2d)
117  self.sliceX = [s//2 for s in self.parent.unitGrid.shape[:-1]]
118  self.sliceX[self.j] = slice(None)
119  self.sliceX = tuple(self.sliceX)
120  self.sliceY = [s//2 for s in self.parent.unitGrid.shape[:-1]]
121  self.sliceY[self.i] = slice(None)
122  self.sliceY = tuple(self.sliceY)
123  self.track = dict(
124  x=numpy.array([iteration.sample.get(self.xKey) for iteration in self.parent.track]),
125  y=numpy.array([iteration.sample.get(self.yKey) for iteration in self.parent.track]),
126  z=numpy.array([iteration.sample.get(self.zKey) for iteration in self.parent.track]),
127  )
128  self.n = n
129  self.figure = matplotlib.pyplot.figure("%s vs %s" % (xDim, yDim), figsize=(16, 8))
130  self.figure.subplots_adjust(left=0.025, right=0.975, bottom=0.08, top=0.95, wspace=0.12)
131  self.axes3d = self.figure.add_subplot(1, 2, 1, projection='3d')
132  self.axes3d.autoscale(False)
133  self.axes3d.set_xlabel(self.xDim)
134  self.axes3d.set_ylabel(self.yDim)
135  self.axes2d = self.figure.add_subplot(1, 2, 2)
136  self.axes2d.set_xlabel(self.xDim)
137  self.axes2d.set_ylabel(self.yDim)
138  self.axes2d.autoscale(False)
139  divider = make_axes_locatable(self.axes2d)
140  self.axesX = divider.append_axes("top", 1.5, pad=0.1, sharex=self.axes2d)
141  self.axesX.autoscale(False, axis='x')
142  hide_xticklabels(self.axesX)
143  self.axesY = divider.append_axes("right", 1.5, pad=0.1, sharey=self.axes2d)
144  self.axesY.autoscale(False, axis='y')
145  hide_yticklabels(self.axesY)
146  self.artists = []
147  self.guessExtent()
148  self.plotTrack()
149  self.plotRejected()
150  self.plotSurfaces()
151 
152  @property
153  def xlim(self):
154  return self._extent[:2]
155 
156  @property
157  def ylim(self):
158  return self._extent[2:4]
159 
160  @property
161  def zlim(self):
162  return self._extent[4:]
163 
164  def guessExtent(self):
165  current = self.parent.track[self.n]
166  x = current.sample.get(self.xKey)
167  y = current.sample.get(self.yKey)
168  zMin1 = current.objectiveValues[self.slice2d].min()
169  zMax1 = current.objectiveValues[self.slice2d].max()
170  zMin2 = current.objectiveModel[self.slice2d].min()
171  zMax2 = current.objectiveModel[self.slice2d].max()
172  self.setExtent(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[0]
179  if x1 is None:
180  x1 = self._extent[1]
181  if y0 is None:
182  y0 = self._extent[2]
183  if y1 is None:
184  y1 = self._extent[3]
185  if z0 is None:
186  z0 = self._extent[4]
187  if z1 is None:
188  z1 = self._extent[5]
189  self._extent = (x0, x1, y0, y1, z0, z1)
190  self._lock = lock
191  self.axes3d.set_xlim(*self.xlim)
192  self.axes3d.set_ylim(*self.ylim)
193  self.axes3d.set_zlim(*self.zlim)
194  self.axes2d.set_xlim(*self.xlim)
195  self.axes2d.set_ylim(*self.ylim)
196  self.axesX.set_ylim(*self.zlim)
197  self.axesY.set_xlim(*self.zlim)
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.xlim[0], x > self.xlim[1],
203  y < self.ylim[0], y > self.ylim[1],
204  z < self.zlim[0], z > self.zlim[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.artists.extend(axes.contour(*args, **kwds).collections)
213 
214  def plotTrack(self):
215  kwds = dict(markeredgewidth=0, markerfacecolor='g', color='g', marker='o')
216  self.axes3d.plot(self.track['x'], self.track['y'], self.track['z'], **kwds)
217  self.axes2d.plot(self.track['x'], self.track['y'], **kwds)
218  self.axesX.plot(self.track['x'], self.track['z'], **kwds)
219  self.axesY.plot(self.track['z'], self.track['y'], **kwds)
220 
221  def plotRejected(self):
222  kwds = dict(markeredgewidth=0, markerfacecolor='r', color='r', marker='v')
223  current = self.parent.track[self.n]
224  cx = current.sample.get(self.xKey)
225  cy = current.sample.get(self.yKey)
226  cz = current.sample.get(self.zKey)
227  for r in current.rejected:
228  x = [cx, r.get(self.xKey)]
229  y = [cy, r.get(self.yKey)]
230  z = [cz, r.get(self.zKey)]
231  self.artists.extend(self.axes3d.plot(x, y, z, **kwds))
232  self.artists.extend(self.axes2d.plot(x, y, **kwds))
233  self.artists.extend(self.axesX.plot(x, z, **kwds))
234  self.artists.extend(self.axesY.plot(z, y, **kwds))
235 
236  def plotSurfaces(self):
237  current = self.parent.track[self.n]
238 
239  # Start with 2-d and 3-d surfaces
240  x = current.grid[self.slice2d + (self.j,)]
241  y = current.grid[self.slice2d + (self.i,)]
242  z1 = current.objectiveValues[self.slice2d].copy()
243  z2 = current.objectiveModel[self.slice2d].copy()
244  norm = matplotlib.colors.Normalize(vmin=self.zlim[0], vmax=self.zlim[1])
245 
246  self._contour(self.axes2d, x, y, z1, cmap=matplotlib.cm.spring, norm=norm)
247  self._contour(self.axes2d, 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(x, y, z1):
251  self._contour(self.axes3d, x, y, z1, cmap=matplotlib.cm.spring, norm=norm)
252  self.artists.append(self.axes3d.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(x, y, z2):
256  self._contour(self.axes3d, x, y, z2, cmap=matplotlib.cm.winter, norm=norm)
257  self.artists.append(self.axes3d.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.artists.extend(self.axesX.plot(current.grid[self.sliceX + (self.j,)],
263  current.objectiveValues[self.sliceX], 'm-'))
264  self.artists.extend(self.axesX.plot(current.grid[self.sliceX + (self.j,)],
265  current.objectiveModel[self.sliceX], 'c-'))
266  self.artists.extend(self.axesY.plot(current.objectiveValues[self.sliceY],
267  current.grid[self.sliceY + (self.i,)], 'm-'))
268  self.artists.extend(self.axesY.plot(current.objectiveModel[self.sliceY],
269  current.grid[self.sliceY + (self.i,)], 'c-'))
270 
271  def move(self, n):
272  self.n = n
273  if not self._lock:
274  self.guessExtent()
275  for artist in self.artists:
276  try:
277  artist.remove()
278  except TypeError:
279  # sometimes matplotlib throws an exception even though everything worked fine
280  pass
281  self.artists = []
282  self.plotSurfaces()
283  self.plotRejected()
284  self.figure.canvas.draw()
def plot(mag, width, centers, clusterId, marker="o", markersize=2, markeredgewidth=0, ltype='-', magType="model", clear=True)
def __init__(self, history, model, objective, steps=11)
std::shared_ptr< FrameSet > append(FrameSet const &first, FrameSet const &second)
Construct a FrameSet that performs two transformations in series.
Definition: functional.cc:33
int min
int max
def setExtent(self, x0=None, x1=None, y0=None, y1=None, z0=None, z1=None, lock=True)
daf::base::PropertyList * list
Definition: fits.cc:903