LSST Applications  21.0.0-172-gfb10e10a+18fedfabac,22.0.0+297cba6710,22.0.0+80564b0ff1,22.0.0+8d77f4f51a,22.0.0+a28f4c53b1,22.0.0+dcf3732eb2,22.0.1-1-g7d6de66+2a20fdde0d,22.0.1-1-g8e32f31+297cba6710,22.0.1-1-geca5380+7fa3b7d9b6,22.0.1-12-g44dc1dc+2a20fdde0d,22.0.1-15-g6a90155+515f58c32b,22.0.1-16-g9282f48+790f5f2caa,22.0.1-2-g92698f7+dcf3732eb2,22.0.1-2-ga9b0f51+7fa3b7d9b6,22.0.1-2-gd1925c9+bf4f0e694f,22.0.1-24-g1ad7a390+a9625a72a8,22.0.1-25-g5bf6245+3ad8ecd50b,22.0.1-25-gb120d7b+8b5510f75f,22.0.1-27-g97737f7+2a20fdde0d,22.0.1-32-gf62ce7b1+aa4237961e,22.0.1-4-g0b3f228+2a20fdde0d,22.0.1-4-g243d05b+871c1b8305,22.0.1-4-g3a563be+32dcf1063f,22.0.1-4-g44f2e3d+9e4ab0f4fa,22.0.1-42-gca6935d93+ba5e5ca3eb,22.0.1-5-g15c806e+85460ae5f3,22.0.1-5-g58711c4+611d128589,22.0.1-5-g75bb458+99c117b92f,22.0.1-6-g1c63a23+7fa3b7d9b6,22.0.1-6-g50866e6+84ff5a128b,22.0.1-6-g8d3140d+720564cf76,22.0.1-6-gd805d02+cc5644f571,22.0.1-8-ge5750ce+85460ae5f3,master-g6e05de7fdc+babf819c66,master-g99da0e417a+8d77f4f51a,w.2021.48
LSST Data Management Base Package
Public Member Functions | Public Attributes | List of all members
lsst.meas.astrom.pessimistic_pattern_matcher_b_3D.PessimisticPatternMatcherB Class Reference

Public Member Functions

def __init__ (self, reference_array, log)
 
def match (self, source_array, n_check, n_match, n_agree, max_n_patterns, max_shift, max_rotation, max_dist, min_matches, pattern_skip_array=None)
 

Public Attributes

 log
 

Detailed Description

Class implementing a pessimistic version of Optimistic Pattern Matcher
B (OPMb) from Tabur 2007. See `DMTN-031 <http://ls.st/DMTN-031`_

Parameters
----------
reference_array : `numpy.ndarray`, (N, 3)
    spherical points x, y, z of to use as reference objects for
    pattern matching.
log : `lsst.log.Log` or `logging.Logger`
    Logger for outputting debug info.

Notes
-----
The class loads and stores the reference object
in a convenient data structure for matching any set of source objects that
are assumed to contain each other. The pessimistic nature of the algorithm
comes from requiring that it discovers at least two patterns that agree on
the correct shift and rotation for matching before exiting. The original
behavior of OPMb can be recovered simply. Patterns matched between the
input datasets are n-spoked pinwheels created from n+1 points. Refer to
DMTN #031 for more details. http://github.com/lsst-dm/dmtn-031

Definition at line 55 of file pessimistic_pattern_matcher_b_3D.py.

Constructor & Destructor Documentation

◆ __init__()

def lsst.meas.astrom.pessimistic_pattern_matcher_b_3D.PessimisticPatternMatcherB.__init__ (   self,
  reference_array,
  log 
)

Definition at line 79 of file pessimistic_pattern_matcher_b_3D.py.

79  def __init__(self, reference_array, log):
80  self._reference_array = reference_array
81  self._n_reference = len(self._reference_array)
82  self.log = log
83 
84  if self._n_reference > 0:
85  self._build_distances_and_angles()
86  else:
87  raise ValueError("No reference objects supplied")
88 

Member Function Documentation

◆ match()

def lsst.meas.astrom.pessimistic_pattern_matcher_b_3D.PessimisticPatternMatcherB.match (   self,
  source_array,
  n_check,
  n_match,
  n_agree,
  max_n_patterns,
  max_shift,
  max_rotation,
  max_dist,
  min_matches,
  pattern_skip_array = None 
)
Match a given source catalog into the loaded reference catalog.

Given array of points on the unit sphere and tolerances, we
attempt to match a pinwheel like pattern between these input sources
and the reference objects this class was created with. This pattern
informs of the shift and rotation needed to align the input source
objects into the frame of the references.

Parameters
----------
source_array : `numpy.ndarray`, (N, 3)
    An array of spherical x,y,z coordinates and a magnitude in units
    of objects having a lower value for sorting. The array should be
    of shape (N, 4).
n_check  : `int`
    Number of sources to create a pattern from. Not all objects may be
    checked if n_match criteria is before looping through all n_check
    objects.
n_match : `int`
    Number of objects to use in constructing a pattern to match.
n_agree : `int`
    Number of found patterns that must agree on their shift and
    rotation before exiting. Set this value to 1 to recover the
    expected behavior of Optimistic Pattern Matcher B.
max_n_patters : `int`
    Number of patterns to create from the input source objects to
    attempt to match into the reference objects.
max_shift : `float`
    Maximum allowed shift to match patterns in arcseconds.
max_rotation : `float`
    Maximum allowed rotation between patterns in degrees.
max_dist : `float`
    Maximum distance in arcseconds allowed between candidate spokes in
    the source and reference objects. Also sets that maximum distance
    in the intermediate verify, pattern shift/rotation agreement, and
    final verify steps.
pattern_skip_array : `int`
    Patterns we would like to skip. This could be due to the pattern
    being matched on a previous iteration that we now consider invalid.
    This assumes the ordering of the source objects is the same
    between different runs of the matcher which, assuming no object
    has been inserted or the magnitudes have changed, it should be.

Returns
-------
output_struct : `lsst.pipe.base.Struct`
    Result struct with components

    - ``matches`` : (N, 2) array of matched ids for pairs. Empty list if no
      match found (`numpy.ndarray`, (N, 2) or `list`)
    - ``distances_rad`` : Radian distances between the matched objects.
      Empty list if no match found (`numpy.ndarray`, (N,))
    - ``pattern_idx``: Index of matched pattern. None if no match found
      (`int`).
    - ``shift`` : Magnitude for the shift between the source and reference
      objects in arcseconds. None if no match found (`float`).

Definition at line 135 of file pessimistic_pattern_matcher_b_3D.py.

137  min_matches, pattern_skip_array=None):
138  """Match a given source catalog into the loaded reference catalog.
139 
140  Given array of points on the unit sphere and tolerances, we
141  attempt to match a pinwheel like pattern between these input sources
142  and the reference objects this class was created with. This pattern
143  informs of the shift and rotation needed to align the input source
144  objects into the frame of the references.
145 
146  Parameters
147  ----------
148  source_array : `numpy.ndarray`, (N, 3)
149  An array of spherical x,y,z coordinates and a magnitude in units
150  of objects having a lower value for sorting. The array should be
151  of shape (N, 4).
152  n_check : `int`
153  Number of sources to create a pattern from. Not all objects may be
154  checked if n_match criteria is before looping through all n_check
155  objects.
156  n_match : `int`
157  Number of objects to use in constructing a pattern to match.
158  n_agree : `int`
159  Number of found patterns that must agree on their shift and
160  rotation before exiting. Set this value to 1 to recover the
161  expected behavior of Optimistic Pattern Matcher B.
162  max_n_patters : `int`
163  Number of patterns to create from the input source objects to
164  attempt to match into the reference objects.
165  max_shift : `float`
166  Maximum allowed shift to match patterns in arcseconds.
167  max_rotation : `float`
168  Maximum allowed rotation between patterns in degrees.
169  max_dist : `float`
170  Maximum distance in arcseconds allowed between candidate spokes in
171  the source and reference objects. Also sets that maximum distance
172  in the intermediate verify, pattern shift/rotation agreement, and
173  final verify steps.
174  pattern_skip_array : `int`
175  Patterns we would like to skip. This could be due to the pattern
176  being matched on a previous iteration that we now consider invalid.
177  This assumes the ordering of the source objects is the same
178  between different runs of the matcher which, assuming no object
179  has been inserted or the magnitudes have changed, it should be.
180 
181  Returns
182  -------
183  output_struct : `lsst.pipe.base.Struct`
184  Result struct with components
185 
186  - ``matches`` : (N, 2) array of matched ids for pairs. Empty list if no
187  match found (`numpy.ndarray`, (N, 2) or `list`)
188  - ``distances_rad`` : Radian distances between the matched objects.
189  Empty list if no match found (`numpy.ndarray`, (N,))
190  - ``pattern_idx``: Index of matched pattern. None if no match found
191  (`int`).
192  - ``shift`` : Magnitude for the shift between the source and reference
193  objects in arcseconds. None if no match found (`float`).
194  """
195 
196  # Given our input source_array we sort on magnitude.
197  sorted_source_array = source_array[source_array[:, -1].argsort(), :3]
198  n_source = len(sorted_source_array)
199 
200  # Initialize output struct.
201  output_match_struct = pipeBase.Struct(
202  match_ids=[],
203  distances_rad=[],
204  pattern_idx=None,
205  shift=None,
206  max_dist_rad=None,)
207 
208  if n_source <= 0:
209  self.log.warning("Source object array is empty. Unable to match. Exiting matcher.")
210  return None
211 
212  # To test if the shifts and rotations we find agree with each other,
213  # we first create two test points situated at the top and bottom of
214  # where the z axis on the sphere bisects the source catalog.
215  test_vectors = self._compute_test_vectors(source_array[:, :3])
216 
217  # We now create an empty list of our resultant rotated vectors to
218  # compare the different rotations we find.
219  rot_vect_list = []
220 
221  # Convert the tolerances to values we will use in the code.
222  max_cos_shift = np.cos(np.radians(max_shift / 3600.))
223  max_cos_rot_sq = np.cos(np.radians(max_rotation)) ** 2
224  max_dist_rad = np.radians(max_dist / 3600.)
225 
226  # Loop through the sources from brightest to faintest, grabbing a
227  # chunk of n_check each time.
228  for pattern_idx in range(np.min((max_n_patterns,
229  n_source - n_match))):
230 
231  # If this pattern is one that we matched on the past but we
232  # now want to skip, we do so here.
233  if pattern_skip_array is not None and \
234  np.any(pattern_skip_array == pattern_idx):
235  self.log.debug(
236  "Skipping previously matched bad pattern %i...",
237  pattern_idx)
238  continue
239  # Grab the sources to attempt to create this pattern.
240  pattern = sorted_source_array[
241  pattern_idx: np.min((pattern_idx + n_check, n_source)), :3]
242 
243  # Construct a pattern given the number of points defining the
244  # pattern complexity. This is the start of the primary tests to
245  # match our source pattern into the reference objects.
246  construct_return_struct = \
247  self._construct_pattern_and_shift_rot_matrix(
248  pattern, n_match, max_cos_shift, max_cos_rot_sq,
249  max_dist_rad)
250 
251  # Our struct is None if we could not match the pattern.
252  if construct_return_struct.ref_candidates is None or \
253  construct_return_struct.shift_rot_matrix is None or \
254  construct_return_struct.cos_shift is None or \
255  construct_return_struct.sin_rot is None:
256  continue
257 
258  # Grab the output data from the Struct object.
259  ref_candidates = construct_return_struct.ref_candidates
260  shift_rot_matrix = construct_return_struct.shift_rot_matrix
261  cos_shift = construct_return_struct.cos_shift
262  sin_rot = construct_return_struct.sin_rot
263 
264  # If we didn't match enough candidates we continue to the next
265  # pattern.
266  if len(ref_candidates) < n_match:
267  continue
268 
269  # Now that we know our pattern and shift/rotation are valid we
270  # store the the rotated versions of our test points for later
271  # use.
272  tmp_rot_vect_list = []
273  for test_vect in test_vectors:
274  tmp_rot_vect_list.append(np.dot(shift_rot_matrix, test_vect))
275  # Since our test point are in the source frame, we can test if
276  # their lengths are mostly preserved under the transform.
277  if not self._test_pattern_lengths(np.array(tmp_rot_vect_list),
278  max_dist_rad):
279  continue
280 
281  tmp_rot_vect_list.append(pattern_idx)
282  rot_vect_list.append(tmp_rot_vect_list)
283 
284  # Test if we have enough rotations, which agree, or if we
285  # are in optimistic mode.
286  if self._test_rotation_agreement(rot_vect_list, max_dist_rad) < \
287  n_agree - 1:
288  continue
289 
290  # Run the final verify step.
291  match_struct = self._final_verify(source_array[:, :3],
292  shift_rot_matrix,
293  max_dist_rad,
294  min_matches)
295  if match_struct.match_ids is None or \
296  match_struct.distances_rad is None or \
297  match_struct.max_dist_rad is None:
298  continue
299 
300  # Convert the observed shift to arcseconds
301  shift = np.degrees(np.arccos(cos_shift)) * 3600.
302  # Print information to the logger.
303  self.log.debug("Succeeded after %i patterns.", pattern_idx)
304  self.log.debug("\tShift %.4f arcsec", shift)
305  self.log.debug("\tRotation: %.4f deg",
306  np.degrees(np.arcsin(sin_rot)))
307 
308  # Fill the struct and return.
309  output_match_struct.match_ids = \
310  match_struct.match_ids
311  output_match_struct.distances_rad = \
312  match_struct.distances_rad
313  output_match_struct.pattern_idx = pattern_idx
314  output_match_struct.shift = shift
315  output_match_struct.max_dist_rad = match_struct.max_dist_rad
316  return output_match_struct
317 
318  self.log.debug("Failed after %i patterns.", pattern_idx + 1)
319  return output_match_struct
320 

Member Data Documentation

◆ log

lsst.meas.astrom.pessimistic_pattern_matcher_b_3D.PessimisticPatternMatcherB.log

Definition at line 82 of file pessimistic_pattern_matcher_b_3D.py.


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