LSST Applications  21.0.0-147-g0e635eb1+1acddb5be5,22.0.0+052faf71bd,22.0.0+1ea9a8b2b2,22.0.0+6312710a6c,22.0.0+729191ecac,22.0.0+7589c3a021,22.0.0+9f079a9461,22.0.1-1-g7d6de66+b8044ec9de,22.0.1-1-g87000a6+536b1ee016,22.0.1-1-g8e32f31+6312710a6c,22.0.1-10-gd060f87+016f7cdc03,22.0.1-12-g9c3108e+df145f6f68,22.0.1-16-g314fa6d+c825727ab8,22.0.1-19-g93a5c75+d23f2fb6d8,22.0.1-19-gb93eaa13+aab3ef7709,22.0.1-2-g8ef0a89+b8044ec9de,22.0.1-2-g92698f7+9f079a9461,22.0.1-2-ga9b0f51+052faf71bd,22.0.1-2-gac51dbf+052faf71bd,22.0.1-2-gb66926d+6312710a6c,22.0.1-2-gcb770ba+09e3807989,22.0.1-20-g32debb5+b8044ec9de,22.0.1-23-gc2439a9a+fb0756638e,22.0.1-3-g496fd5d+09117f784f,22.0.1-3-g59f966b+1e6ba2c031,22.0.1-3-g849a1b8+f8b568069f,22.0.1-3-gaaec9c0+c5c846a8b1,22.0.1-32-g5ddfab5d3+60ce4897b0,22.0.1-4-g037fbe1+64e601228d,22.0.1-4-g8623105+b8044ec9de,22.0.1-5-g096abc9+d18c45d440,22.0.1-5-g15c806e+57f5c03693,22.0.1-7-gba73697+57f5c03693,master-g6e05de7fdc+c1283a92b8,master-g72cdda8301+729191ecac,w.2021.39
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`
    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 53 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 77 of file pessimistic_pattern_matcher_b_3D.py.

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

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 166 of file pessimistic_pattern_matcher_b_3D.py.

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

Member Data Documentation

◆ log

lsst.meas.astrom.pessimistic_pattern_matcher_b_3D.PessimisticPatternMatcherB.log

Definition at line 80 of file pessimistic_pattern_matcher_b_3D.py.


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