LSST Applications g0f08755f38+9c285cab97,g1635faa6d4+13f3999e92,g1653933729+a8ce1bb630,g1a0ca8cf93+bf6eb00ceb,g28da252d5a+0829b12dee,g29321ee8c0+5700dc9eac,g2bbee38e9b+9634bc57db,g2bc492864f+9634bc57db,g2cdde0e794+c2c89b37c4,g3156d2b45e+41e33cbcdc,g347aa1857d+9634bc57db,g35bb328faa+a8ce1bb630,g3a166c0a6a+9634bc57db,g3e281a1b8c+9f2c4e2fc3,g414038480c+077ccc18e7,g41af890bb2+fde0dd39b6,g5fbc88fb19+17cd334064,g781aacb6e4+a8ce1bb630,g80478fca09+55a9465950,g82479be7b0+d730eedb7d,g858d7b2824+9c285cab97,g9125e01d80+a8ce1bb630,g9726552aa6+10f999ec6a,ga5288a1d22+2a84bb7594,gacf8899fa4+c69c5206e8,gae0086650b+a8ce1bb630,gb58c049af0+d64f4d3760,gc28159a63d+9634bc57db,gcf0d15dbbd+4b7d09cae4,gda3e153d99+9c285cab97,gda6a2b7d83+4b7d09cae4,gdaeeff99f8+1711a396fd,ge2409df99d+5e831397f4,ge79ae78c31+9634bc57db,gf0baf85859+147a0692ba,gf3967379c6+41c94011de,gf3fb38a9a8+8f07a9901b,gfb92a5be7c+9c285cab97,w.2024.46
LSST Data Management Base Package
Loading...
Searching...
No Matches
Public Member Functions | Public Attributes | Protected Member Functions | List of all members
lsst.scarlet.lite.blend.Blend Class Reference
Inheritance diagram for lsst.scarlet.lite.blend.Blend:
lsst.scarlet.lite.models.fit_psf.FittedPsfBlend

Public Member Functions

 __init__ (self, Sequence[Source] sources, Observation observation)
 
tuple[int, int, int] shape (self)
 
Box bbox (self)
 
list[Componentcomponents (self)
 
Image get_model (self, bool convolve=False, bool use_flux=False)
 
float log_likelihood (self)
 
Blend fit_spectra (self, bool clip=False)
 
tuple[int, float] fit (self, int max_iter, float e_rel=1e-4, int min_iter=15, int resize=10)
 
 parameterize (self, Callable parameterization)
 
None conserve_flux (self, bool mask_footprint=True)
 

Public Attributes

 sources
 
 observation
 
 it
 
 shape
 
 bbox
 

Protected Member Functions

Image _grad_log_likelihood (self)
 

Detailed Description

A single blend.

This class holds all of the sources and observation that are to be fit,
as well as performing fitting and joint initialization of the
spectral components (when applicable).

Parameters
----------
sources:
    The sources to fit.
observation:
    The observation that contains the images,
        PSF, etc. that are being fit.

Definition at line 37 of file blend.py.

Constructor & Destructor Documentation

◆ __init__()

lsst.scarlet.lite.blend.Blend.__init__ ( self,
Sequence[Source] sources,
Observation observation )

Definition at line 53 of file blend.py.

53 def __init__(self, sources: Sequence[Source], observation: Observation):
54 self.sources = list(sources)
55 self.observation = observation
56
57 # Initialize the iteration count and loss function
58 self.it = 0
59 self.loss: list[float] = []
60

Member Function Documentation

◆ _grad_log_likelihood()

Image lsst.scarlet.lite.blend.Blend._grad_log_likelihood ( self)
protected
Gradient of the likelihood wrt the unconvolved model

Reimplemented in lsst.scarlet.lite.models.fit_psf.FittedPsfBlend.

Definition at line 116 of file blend.py.

116 def _grad_log_likelihood(self) -> Image:
117 """Gradient of the likelihood wrt the unconvolved model"""
118 model = self.get_model(convolve=True)
119 # Update the loss
120 self.loss.append(self.observation.log_likelihood(model))
121 # Calculate the gradient wrt the model d(logL)/d(model)
122 result = self.observation.weights * (model - self.observation.images)
123 result = self.observation.convolve(result, grad=True)
124 return result
125

◆ bbox()

Box lsst.scarlet.lite.blend.Blend.bbox ( self)
The bounding box of the entire blend.

Definition at line 67 of file blend.py.

67 def bbox(self) -> Box:
68 """The bounding box of the entire blend."""
69 return self.observation.bbox
70
AmpInfoBoxKey bbox
Definition Amplifier.cc:117

◆ components()

list[Component] lsst.scarlet.lite.blend.Blend.components ( self)
The list of all components in the blend.

Since the list of sources might change,
this is always built on the fly.

Definition at line 72 of file blend.py.

72 def components(self) -> list[Component]:
73 """The list of all components in the blend.
74
75 Since the list of sources might change,
76 this is always built on the fly.
77 """
78 return [c for src in self.sources for c in src.components]
79
table::Key< table::Array< int > > components

◆ conserve_flux()

None lsst.scarlet.lite.blend.Blend.conserve_flux ( self,
bool mask_footprint = True )
Use the source models as templates to re-distribute flux
from the data

The source models are used as approximations to the data,
which redistribute the flux in the data according to the
ratio of the models for each source.
There is no return value for this function,
instead it adds (or modifies) a ``flux_weighted_image``
attribute to each the sources with the flux attributed to
that source.

Parameters
----------
blend:
    The blend that is being fit
mask_footprint:
    Whether or not to apply a mask for pixels with zero weight.

Definition at line 270 of file blend.py.

270 def conserve_flux(self, mask_footprint: bool = True) -> None:
271 """Use the source models as templates to re-distribute flux
272 from the data
273
274 The source models are used as approximations to the data,
275 which redistribute the flux in the data according to the
276 ratio of the models for each source.
277 There is no return value for this function,
278 instead it adds (or modifies) a ``flux_weighted_image``
279 attribute to each the sources with the flux attributed to
280 that source.
281
282 Parameters
283 ----------
284 blend:
285 The blend that is being fit
286 mask_footprint:
287 Whether or not to apply a mask for pixels with zero weight.
288 """
289 observation = self.observation
290 py = observation.psfs.shape[-2] // 2
291 px = observation.psfs.shape[-1] // 2
292
293 images = observation.images.copy()
294 if mask_footprint:
295 images.data[observation.weights.data == 0] = 0
296 model = self.get_model()
297 # Always convolve in real space to avoid FFT artifacts
298 model = observation.convolve(model, mode="real")
299 model.data[model.data < 0] = 0
300
301 for src in self.sources:
302 if src.is_null:
303 src.flux_weighted_image = Image.from_box(Box((0, 0)), bands=observation.bands) # type: ignore
304 continue
305 src_model = src.get_model()
306
307 # Grow the model to include the wings of the PSF
308 src_box = src.bbox.grow((py, px))
309 overlap = observation.bbox & src_box
310 src_model = src_model.project(bbox=overlap)
311 src_model = observation.convolve(src_model, mode="real")
312 src_model.data[src_model.data < 0] = 0
313 numerator = src_model.data
314 denominator = model[overlap].data
315 cuts = denominator != 0
316 ratio = np.zeros(numerator.shape, dtype=numerator.dtype)
317 ratio[cuts] = numerator[cuts] / denominator[cuts]
318 ratio[denominator == 0] = 0
319 # sometimes numerical errors can cause a hot pixel to have a
320 # slightly higher ratio than 1
321 ratio[ratio > 1] = 1
322 src.flux_weighted_image = src_model.copy_with(data=ratio) * images[overlap]

◆ fit()

tuple[int, float] lsst.scarlet.lite.blend.Blend.fit ( self,
int max_iter,
float e_rel = 1e-4,
int min_iter = 15,
int resize = 10 )
Fit all of the parameters

Parameters
----------
max_iter:
    The maximum number of iterations
e_rel:
    The relative error to use for determining convergence.
min_iter:
    The minimum number of iterations.
resize:
    Number of iterations before attempting to resize the
    resizable components. If `resize` is `None` then
    no resizing is ever attempted.

Returns
-------
it:
    Number of iterations.
loss:
    Loss for the last solution

Reimplemented in lsst.scarlet.lite.models.fit_psf.FittedPsfBlend.

Definition at line 208 of file blend.py.

214 ) -> tuple[int, float]:
215 """Fit all of the parameters
216
217 Parameters
218 ----------
219 max_iter:
220 The maximum number of iterations
221 e_rel:
222 The relative error to use for determining convergence.
223 min_iter:
224 The minimum number of iterations.
225 resize:
226 Number of iterations before attempting to resize the
227 resizable components. If `resize` is `None` then
228 no resizing is ever attempted.
229
230 Returns
231 -------
232 it:
233 Number of iterations.
234 loss:
235 Loss for the last solution
236 """
237 while self.it < max_iter:
238 # Calculate the gradient wrt the on-convolved model
239 grad_log_likelihood = self._grad_log_likelihood()
240 if resize is not None and self.it > 0 and self.it % resize == 0:
241 do_resize = True
242 else:
243 do_resize = False
244 # Update each component given the current gradient
245 for component in self.components:
246 overlap = component.bbox & self.bbox
247 component.update(self.it, grad_log_likelihood[overlap].data)
248 # Check to see if any components need to be resized
249 if do_resize:
250 component.resize(self.bbox)
251 # Stopping criteria
252 self.it += 1
253 if self.it > min_iter and np.abs(self.loss[-1] - self.loss[-2]) < e_rel * np.abs(self.loss[-1]):
254 break
255 return self.it, self.loss[-1]
256

◆ fit_spectra()

Blend lsst.scarlet.lite.blend.Blend.fit_spectra ( self,
bool clip = False )
Fit all of the spectra given their current morphologies with a
linear least squares algorithm.

Parameters
----------
clip:
    Whether or not to clip components that were not
    assigned any flux during the fit.

Returns
-------
blend:
    The blend with updated components is returned.

Definition at line 135 of file blend.py.

135 def fit_spectra(self, clip: bool = False) -> Blend:
136 """Fit all of the spectra given their current morphologies with a
137 linear least squares algorithm.
138
139 Parameters
140 ----------
141 clip:
142 Whether or not to clip components that were not
143 assigned any flux during the fit.
144
145 Returns
146 -------
147 blend:
148 The blend with updated components is returned.
149 """
150 from .initialization import multifit_spectra
151
152 morphs = []
153 spectra = []
154 factorized_indices = []
155 model = Image.from_box(
156 self.observation.bbox,
157 bands=self.observation.bands,
158 dtype=self.observation.dtype,
159 )
160 components = self.components
161 for idx, component in enumerate(components):
162 if hasattr(component, "morph") and hasattr(component, "spectrum"):
163 component = cast(FactorizedComponent, component)
164 morphs.append(component.morph)
165 spectra.append(component.spectrum)
166 factorized_indices.append(idx)
167 else:
168 model.insert(component.get_model())
169 model = self.observation.convolve(model, mode="real")
170
171 boxes = [c.bbox for c in components]
172 fit_spectra = multifit_spectra(
173 self.observation,
174 [Image(morph, yx0=cast(tuple[int, int], bbox.origin)) for morph, bbox in zip(morphs, boxes)],
175 model,
176 )
177 for idx in range(len(morphs)):
178 component = cast(FactorizedComponent, components[factorized_indices[idx]])
179 component.spectrum[:] = fit_spectra[idx]
180 component.spectrum[component.spectrum < 0] = 0
181
182 # Run the proxes for all of the components to make sure that the
183 # spectra are consistent with the constraints.
184 # In practice this usually means making sure that they are
185 # non-negative.
186 for src in self.sources:
187 for component in src.components:
188 if (
189 hasattr(component, "spectrum")
190 and hasattr(component, "prox_spectrum")
191 and component.prox_spectrum is not None # type: ignore
192 ):
193 component.prox_spectrum(component.spectrum) # type: ignore
194
195 if clip:
196 # Remove components with no positive flux
197 for src in self.sources:
198 _components = []
199 for component in src.components:
200 component_model = component.get_model()
201 component_model.data[component_model.data < 0] = 0
202 if np.sum(component_model.data) > 0:
203 _components.append(component)
204 src.components = _components
205
206 return self
207

◆ get_model()

Image lsst.scarlet.lite.blend.Blend.get_model ( self,
bool convolve = False,
bool use_flux = False )
Generate a model of the entire blend.

Parameters
----------
convolve:
    Whether to convolve the model with the observed PSF in each band.
use_flux:
    Whether to use the re-distributed flux associated with the sources
    instead of the component models.

Returns
-------
model:
    The model created by combining all of the source models.

Definition at line 80 of file blend.py.

80 def get_model(self, convolve: bool = False, use_flux: bool = False) -> Image:
81 """Generate a model of the entire blend.
82
83 Parameters
84 ----------
85 convolve:
86 Whether to convolve the model with the observed PSF in each band.
87 use_flux:
88 Whether to use the re-distributed flux associated with the sources
89 instead of the component models.
90
91 Returns
92 -------
93 model:
94 The model created by combining all of the source models.
95 """
96 model = Image(
97 np.zeros(self.shape, dtype=self.observation.images.dtype),
98 bands=self.observation.bands,
99 yx0=cast(tuple[int, int], self.observation.bbox.origin[-2:]),
100 )
101
102 if use_flux:
103 for src in self.sources:
104 if src.flux_weighted_image is None:
105 raise ValueError(
106 "Some sources do not have 'flux' attribute set. Run measure.conserve_flux"
107 )
108 src.flux_weighted_image.insert_into(model)
109 else:
110 for component in self.components:
111 component.get_model().insert_into(model)
112 if convolve:
113 return self.observation.convolve(model)
114 return model
115

◆ log_likelihood()

float lsst.scarlet.lite.blend.Blend.log_likelihood ( self)
The current log-likelihood

This is calculated on the fly to ensure that it is always up to date
with the current model parameters.

Definition at line 127 of file blend.py.

127 def log_likelihood(self) -> float:
128 """The current log-likelihood
129
130 This is calculated on the fly to ensure that it is always up to date
131 with the current model parameters.
132 """
133 return self.observation.log_likelihood(self.get_model(convolve=True))
134

◆ parameterize()

lsst.scarlet.lite.blend.Blend.parameterize ( self,
Callable parameterization )
Convert the component parameter arrays into Parameter instances

Parameters
----------
parameterization:
    A function to use to convert parameters of a given type into
    a `Parameter` in place. It should take a single argument that
    is the `Component` or `Source` that is to be parameterized.

Definition at line 257 of file blend.py.

257 def parameterize(self, parameterization: Callable):
258 """Convert the component parameter arrays into Parameter instances
259
260 Parameters
261 ----------
262 parameterization:
263 A function to use to convert parameters of a given type into
264 a `Parameter` in place. It should take a single argument that
265 is the `Component` or `Source` that is to be parameterized.
266 """
267 for source in self.sources:
268 source.parameterize(parameterization)
269

◆ shape()

tuple[int, int, int] lsst.scarlet.lite.blend.Blend.shape ( self)
Shape of the model for the entire `Blend`.

Definition at line 62 of file blend.py.

62 def shape(self) -> tuple[int, int, int]:
63 """Shape of the model for the entire `Blend`."""
64 return self.observation.shape
65

Member Data Documentation

◆ bbox

lsst.scarlet.lite.blend.Blend.bbox

Definition at line 250 of file blend.py.

◆ it

lsst.scarlet.lite.blend.Blend.it

Definition at line 58 of file blend.py.

◆ observation

lsst.scarlet.lite.blend.Blend.observation

Definition at line 55 of file blend.py.

◆ shape

lsst.scarlet.lite.blend.Blend.shape

Definition at line 97 of file blend.py.

◆ sources

lsst.scarlet.lite.blend.Blend.sources

Definition at line 54 of file blend.py.


The documentation for this class was generated from the following file: