Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 0 additions & 3 deletions doc/changes/DM-47738.feature.rst

This file was deleted.

1 change: 0 additions & 1 deletion doc/changes/DM-49537.md

This file was deleted.

38 changes: 38 additions & 0 deletions doc/lsst.scarlet.lite/changes.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
.. _lsst.scarlet.lite-changes:

=================
v30.0.0 Changes
=================

Improved Slicing
----------------

In v29.0 slicing was only supported for the ``Image`` class. In ``v30.0`` slicing has been extended to ``Blend``, ``Source``, ``Component``, and ``Observation`` classes. This allows uers to use a subset of bands or change the band order of each of these classes. ``Observation`` can be sliced along the spatial dimension as well.

New Image methods
-----------------
- ``Image.trimmed`` method added to remove data below a threshold from an Image.
- ``Image.at`` method added to extract a single pixel from an Image.

Serialization Improvments
-------------------------
Serialization received a major update in ``v30.0`` to improve performance and usability. The major upgrade is a ``Migration Registry`` that registers all scarlet serializable classes and allows users to register their own custom classes along with function to migrate between different versions of the class.
This allows scarlet to automatically handle versioning of serialized objects and migrate them to the latest version when deserializing.

As part of this update the classes used for serialization were expanded to included base classes such as ``BlendBaseData``, ``SourceBaseData``, and ``ComponentBaseData`` to make it easier for users to extend serialization to their own custom classes.

Copying and Deep Copying
------------------------
To support the serialization improvements, ``__copy__`` and ``__deepcopy__`` methods were added to nearly all scarlet lite classes. This allows users to create copies of scarlet objects using the standard library ``copy`` and ``deepcopy`` modules.

Initialization Changes
----------------------
Initialization has a few changes to make it both more customizable and more standardized
- The ineffective ``FactorizedWaveletInitialization`` was deprecated and the ``FactorizedChirInitialization`` was changed to ``FactorizedInitialization`` as testing has shown that it is a superior algorithm.
- A large number of new parameters were added to ``FactorizedInitialization`` to make it easier for users to initialize sources using more tunable constraints and detection images that may differ from the observation image.

Other Changes
-------------
- The detection algorithms have been updated and while not ready for production users should see improved performance.
- A more customizable ``conserve_flux`` function was added to ``scarlet``
- Positions are rounded instead of truncated during initialization to remove a bias from inaccurate sources positions.
1 change: 1 addition & 0 deletions doc/lsst.scarlet.lite/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ toctree linking to topics related to using the module's APIs.

getting_started
detection
changes

.. _lsst.scarlet.lite-contributing:

Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ test = [
"pytest >= 3.2",
]
yaml = ["pyyaml >= 5.1"]
plotting = ["matplotlib", "astropy < 7"]
plotting = ["matplotlib", "astropy >= 6.1"]

[tool.setuptools.packages.find]
where = ["python"]
Expand Down
12 changes: 11 additions & 1 deletion python/lsst/scarlet/lite/bbox.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,8 @@

__all__ = ["Box", "overlapped_slices"]

from typing import Sequence, cast
from copy import deepcopy
from typing import Any, Sequence, cast

import numpy as np

Expand Down Expand Up @@ -462,6 +463,15 @@ def __matmul__(self, bbox: Box) -> Box:
result = Box.from_bounds(*bounds)
return result

def __deepcopy__(self, memo: dict[int, Any]) -> Box:
"""Deep copy of the box"""
my_id = id(self)
if my_id in memo:
return memo[my_id]
result = Box(deepcopy(self.shape), origin=deepcopy(self.origin))
memo[my_id] = result
return result

def __copy__(self) -> Box:
"""Copy of the box"""
return Box(self.shape, origin=self.origin)
Expand Down
141 changes: 138 additions & 3 deletions python/lsst/scarlet/lite/blend.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,15 +24,16 @@
__all__ = ["Blend"]

from abc import ABC, abstractmethod
from typing import TYPE_CHECKING, Any, Callable, Sequence, cast
from copy import deepcopy
from typing import TYPE_CHECKING, Any, Callable, Self, Sequence, cast

import numpy as np

from .bbox import Box
from .component import Component, FactorizedComponent
from .image import Image
from .observation import Observation
from .source import Source
from .source import Source, SourceBase

if TYPE_CHECKING:
from .io import ScarletBlendData, ScarletSourceBaseData
Expand All @@ -57,7 +58,7 @@ class BlendBase(ABC):
Additional metadata to store with the blend.
"""

sources: list[Source]
sources: Sequence[SourceBase]
observation: Observation
metadata: dict | None

Expand All @@ -80,6 +81,72 @@ def components(self) -> list[Component]:
"""
return [c for src in self.sources for c in src.components]

@abstractmethod
def __getitem__(self, indices: Any) -> Self:
"""Get a sub-blend corresponding to the given indices.

Parameters
----------
indices :
The indices to use to slice the blend.

Returns
-------
sub_blend :
A new `BlendBase` instance containing only data from the
specified bands in the specified order.

Raises
------
IndexError :
If the indices contain bands not included in the original
blend or any spatial indices are given.
"""

@abstractmethod
def __copy__(self) -> Self:
"""Create a copy of this blend.

Returns
-------
blend : BlendBase
A new blend that is a copy of this one.
"""

@abstractmethod
def __deepcopy__(self, memo: dict[int, Any]) -> Self:
"""Create a deep copy of this blend.

Parameters
----------
memo : dict[int, Any]
A memoization dictionary used by `copy.deepcopy`.

Returns
-------
blend : BlendBase
A new blend that is a deep copy of this one.
"""

def copy(self, deep: bool = False) -> Self:
"""Create a copy of this blend.

Parameters
----------
deep :
If `True`, a deep copy is made. If `False`, a shallow copy is made.
Default is `False`.

Returns
-------
blend : Self
A new blend that is a copy of this one.
"""
if deep:
return self.__deepcopy__({})
else:
return self.__copy__()

@abstractmethod
def get_model(self, convolve: bool = False, use_flux: bool = False) -> Image:
"""Generate a model of the entire blend.
Expand Down Expand Up @@ -128,6 +195,8 @@ class Blend(BlendBase):
Additional metadata to store with the blend.
"""

sources: list[Source]

def __init__(self, sources: Sequence[Source], observation: Observation, metadata: dict | None = None):
self.sources = list(sources)
self.observation = observation
Expand Down Expand Up @@ -433,3 +502,69 @@ def to_data(self) -> ScarletBlendData:
)

return blend_data

def __getitem__(self, indices: Any) -> Blend:
"""Get a sub-blend corresponding to the given indices.

Parameters
----------
indices :
The indices to use to slice the blend.

Returns
-------
blend :
A new `Blend` instance containing only data from the
specified bands in the specified order.

Raises
------
IndexError :
If the indices contain bands not included in the original
blend or a bounding box is given.
"""
return Blend(
sources=[src[indices] for src in self.sources],
observation=self.observation[indices],
metadata=self.metadata,
)

def __copy__(self) -> Blend:
"""Create a copy of this blend.

Returns
-------
blend : Blend
A new blend that is a copy of this one.
"""
return Blend(sources=self.sources, observation=self.observation, metadata=self.metadata)

def __deepcopy__(self, memo: dict[int, Any]) -> Blend:
"""Create a deep copy of this blend.

Parameters
----------
memo : dict[int, Any]
A memoization dictionary used by `copy.deepcopy`.

Returns
-------
blend : Blend
A new blend that is a deep copy of this one.
"""
# Check if already copied
if id(self) in memo:
return memo[id(self)]

# Create placeholder and add to memo FIRST
blend = Blend.__new__(Blend)
memo[id(self)] = blend

# Now safely initialize the placeholder with deepcopied arguments
blend.__init__( # type: ignore[misc]
sources=[deepcopy(src, memo) for src in self.sources],
observation=deepcopy(self.observation, memo),
metadata=deepcopy(self.metadata, memo),
)

return blend
Loading
Loading