def testShift(self): """ Test the matcher when a shift is applied to the data. We say shift here as while we are rotating the unit-sphere in 3D, on our 'focal plane' this will appear as a shift. """ self.pyPPMb = PessimisticPatternMatcherB( reference_array=self.reference_obj_array[:, :3], log=self.log) theta = np.radians(45.0 / 3600.) cos_theta = np.cos(theta) sin_theta = np.sin(theta) theta_rotation = self.pyPPMb._create_spherical_rotation_matrix( np.array([0, 0, 1]), cos_theta, sin_theta) self.source_obj_array[:, :3] = np.dot( theta_rotation, self.source_obj_array[:, :3].transpose()).transpose() match_struct = self.pyPPMb.match(source_array=self.source_obj_array, n_check=9, n_match=6, n_agree=2, max_n_patterns=100, max_shift=60, max_rotation=5.0, max_dist=5., min_matches=30, pattern_skip_array=None) self.assertEqual(len(match_struct.match_ids), len(self.reference_obj_array)) self.assertTrue( np.all( match_struct.distances_rad < 0.01 / 3600.0 * __deg_to_rad__))
def testRotation(self): """ Test the matcher for when a roation is applied to the data. """ self.pyPPMb = PessimisticPatternMatcherB( reference_array=self.reference_obj_array[:, :3], log=self.log) phi = 2.5 * __deg_to_rad__ cos_phi = np.cos(phi) sin_phi = np.sin(phi) phi_rotation = self.pyPPMb._create_spherical_rotation_matrix( np.array([1, 0, 0]), cos_phi, sin_phi) self.source_obj_array[:, :3] = np.dot( phi_rotation, self.source_obj_array[:, :3].transpose()).transpose() match_struct = self.pyPPMb.match(source_array=self.source_obj_array, n_check=9, n_match=6, n_agree=2, max_n_patterns=100, max_shift=60, max_rotation=5.0, max_dist=5., min_matches=30, pattern_skip_array=None) self.assertEqual(len(match_struct.match_ids), len(self.reference_obj_array)) self.assertTrue( np.all( match_struct.distances_rad < 0.01 / 3600.0 * __deg_to_rad__))
def testLinearDistortion(self): """ Create a simple linear distortion and test that the correct references are still matched. """ self.pyPPMb = PessimisticPatternMatcherB( reference_array=self.reference_obj_array[:, :3], log=self.log) max_z = np.cos(np.pi/2 + 0.5 * __deg_to_rad__) min_z = np.cos(np.pi/2 - 0.5 * __deg_to_rad__) # The max shift in position we add to the position will be 25 # arcseconds. max_distort = 25.0 / 3600. * __deg_to_rad__ self.source_obj_array[:, 2] = ( self.source_obj_array[:, 2] - max_distort * (self.source_obj_array[:, 2] - min_z) / (max_z - min_z)) # Renomalize the 3 vectors to be unit length. distorted_dists = np.sqrt(self.source_obj_array[:, 0] ** 2 + self.source_obj_array[:, 1] ** 2 + self.source_obj_array[:, 2] ** 2) self.source_obj_array[:, 0] /= distorted_dists self.source_obj_array[:, 1] /= distorted_dists self.source_obj_array[:, 2] /= distorted_dists match_struct = self.pyPPMb.match( source_array=self.source_obj_array, n_check=9, n_match=6, n_agree=2, max_n_patterns=100, max_shift=60., max_rotation=5.0, max_dist=5., min_matches=30, pattern_skip_array=None) self.assertEqual(len(match_struct.match_ids), len(self.reference_obj_array)) self.assertTrue( np.all(match_struct.distances_rad < 10 / 3600.0 * __deg_to_rad__))
def testShiftRotation(self): """ Test both a shift and rotation being applied to the data. """ self.pyPPMb = PessimisticPatternMatcherB( reference_array=self.reference_obj_array[:, :3], log=self.log) theta = np.radians(45.0 / 3600.) cos_theta = np.cos(theta) sin_theta = np.sin(theta) theta_rotation = self.pyPPMb._create_spherical_rotation_matrix( np.array([0, 0, 1]), cos_theta, sin_theta) phi = 2.5 * __deg_to_rad__ cos_phi = np.cos(phi) sin_phi = np.sin(phi) phi_rotation = self.pyPPMb._create_spherical_rotation_matrix( np.array([1, 0, 0]), cos_phi, sin_phi) shift_rot_matrix = np.dot(theta_rotation, phi_rotation) self.source_obj_array[:, :3] = np.dot( shift_rot_matrix, self.source_obj_array[:, :3].transpose()).transpose() match_struct = self.pyPPMb.match( source_array=self.source_obj_array, n_check=9, n_match=6, n_agree=2, max_n_patterns=100, max_shift=60., max_rotation=5.0, max_dist=5., min_matches=30, pattern_skip_array=None) self.assertEqual(len(match_struct.match_ids), len(self.reference_obj_array)) self.assertTrue( np.all(match_struct.distances_rad < 0.01/3600.0 * __deg_to_rad__))
def testNoReferenceSources(self): """Check that we get a helpful error when no reference objects are supplied. """ with self.assertRaisesRegex(ValueError, "No reference objects supplied"): PessimisticPatternMatcherB(np.ndarray((0, 3)), self.log)
def testMatchMoreReferences(self): """ Test the case where we have more references than sources but no rotation or shift. """ self.pyPPMb = PessimisticPatternMatcherB( reference_array=self.reference_obj_array[:, :3], log=self.log) match_struct = self.pyPPMb.match( source_array=self.source_obj_array[:500], n_check=9, n_match=6, n_agree=2, max_n_patterns=100, max_shift=60., max_rotation=1.0, max_dist=5., min_matches=30, pattern_skip_array=None) self.assertEqual(len(match_struct.match_ids), len(self.reference_obj_array[:500])) self.assertTrue( np.all(match_struct.distances_rad < 0.01/3600.0 * __deg_to_rad__))
def testMatchSkip(self): """ Test the ability to skip specified patterns in the matching process. """ self.pyPPMb = PessimisticPatternMatcherB( reference_array=self.reference_obj_array[:, :3], log=self.log) match_struct = self.pyPPMb.match( source_array=self.source_obj_array, n_check=9, n_match=6, n_agree=2, max_n_patterns=100, max_shift=60., max_rotation=5.0, max_dist=5., min_matches=30, pattern_skip_array=np.array([0])) self.assertEqual(len(match_struct.match_ids), len(self.reference_obj_array)) self.assertTrue( np.all(match_struct.distances_rad < 0.01/3600.0 * __deg_to_rad__))
def testMatchPerfect(self): """ Input objects that have no shift or rotation to the matcher and test that we return a match. """ self.pyPPMb = PessimisticPatternMatcherB( reference_array=self.reference_obj_array[:, :3], log=self.log) match_struct = self.pyPPMb.match( source_array=self.source_obj_array, n_check=9, n_match=6, n_agree=2, max_n_patterns=100, max_shift=60., max_rotation=5.0, max_dist=5., min_matches=30, pattern_skip_array=None) self.assertEqual(len(match_struct.match_ids), len(self.reference_obj_array)) self.assertTrue( np.all(match_struct.distances_rad < 0.01/3600.0 * __deg_to_rad__))
def testOptimisticMatch(self): """ Test the optimistic mode of the pattern matcher. That is the algorithm with the early exit strategy as described in Tabur 2007. """ self.pyPPMb = PessimisticPatternMatcherB( reference_array=self.reference_obj_array[:, :3], log=self.log) match_struct = self.pyPPMb.match( source_array=self.source_obj_array, n_check=9, n_match=6, n_agree=1, max_n_patterns=100, max_shift=60., max_rotation=6.0, max_dist=5., min_matches=30, pattern_skip_array=None) self.assertEqual(len(match_struct.match_ids), len(self.reference_obj_array)) self.assertTrue( np.all(match_struct.distances_rad < 0.01/3600.0 * __deg_to_rad__))
def testConstructPattern(self): """ Test that a specified pattern can be found in the reference data and that the explicit ids match. """ self.pyPPMb = PessimisticPatternMatcherB( reference_array=self.reference_obj_array[:, :3], log=self.log) pattern_struct = self.pyPPMb._construct_pattern_and_shift_rot_matrix( self.source_obj_array[:6, :3], 6, np.cos(np.radians(60. / 3600.)), np.cos(np.radians(1.0)) ** 2, np.radians(5./3600.)) pattern_list = pattern_struct.ref_candidates self.assertGreater(len(pattern_list), 0) self.assertEqual(pattern_list[0], 0) self.assertEqual(pattern_list[1], 1) self.assertEqual(pattern_list[2], 2) self.assertEqual(pattern_list[3], 3) self.assertEqual(pattern_list[4], 4) self.assertEqual(pattern_list[5], 5) pattern_struct = self.pyPPMb._construct_pattern_and_shift_rot_matrix( self.source_obj_array[:9, :3], 6, np.cos(np.radians(60. / 3600.)), np.cos(np.radians(1.0)) ** 2, np.radians(5./3600.)) pattern_list = pattern_struct.ref_candidates self.assertGreater(len(pattern_list), 0) self.assertEqual(pattern_list[0], 0) self.assertEqual(pattern_list[1], 1) self.assertEqual(pattern_list[2], 2) self.assertEqual(pattern_list[3], 3) self.assertEqual(pattern_list[4], 4) self.assertEqual(pattern_list[5], 5) pattern_struct = self.pyPPMb._construct_pattern_and_shift_rot_matrix( self.source_obj_array[[2, 4, 8, 16, 32, 64], :3], 6, np.cos(np.radians(60. / 3600.)), np.cos(np.radians(1.0)) ** 2, np.radians(5./3600.)) pattern_list = pattern_struct.ref_candidates self.assertEqual(pattern_list[0], 2) self.assertEqual(pattern_list[1], 4) self.assertEqual(pattern_list[2], 8) self.assertEqual(pattern_list[3], 16) self.assertEqual(pattern_list[4], 32) self.assertEqual(pattern_list[5], 64)
class TestPessimisticPatternMatcherB(unittest.TestCase): """Unittest suite for the Pessimistic Pattern Matcher B. """ def setUp(self): np.random.seed(12345) n_points = 1000 # reference_obj_array is a number array representing # 3D points randomly draw on a 1 sq deg patch. self.reference_obj_array = np.empty((n_points, 4)) cos_theta_array = np.random.uniform( np.cos(np.pi / 2 + 0.5 * __deg_to_rad__), np.cos(np.pi / 2 - 0.5 * __deg_to_rad__), size=n_points) sin_theta_array = np.sqrt(1 - cos_theta_array**2) phi_array = np.random.uniform(-0.5, 0.5, size=n_points) * __deg_to_rad__ self.reference_obj_array[:, 0] = sin_theta_array * np.cos(phi_array) self.reference_obj_array[:, 1] = sin_theta_array * np.sin(phi_array) self.reference_obj_array[:, 2] = cos_theta_array self.reference_obj_array[:, 3] = ( np.random.power(1.2, size=n_points) * 4 + 20) # Our initial source catalog is a straight copy of the reference # array at first. In some of the tests we will add rotations and # shifts to the data in order to test the input and outputs of our # matcher. self.source_obj_array = copy(self.reference_obj_array) self.log = logging.getLogger(__name__) def testConstructPattern(self): """ Test that a specified pattern can be found in the reference data and that the explicit ids match. """ self.pyPPMb = PessimisticPatternMatcherB( reference_array=self.reference_obj_array[:, :3], log=self.log) pattern_struct = self.pyPPMb._construct_pattern_and_shift_rot_matrix( self.source_obj_array[:6, :3], 6, np.cos(np.radians(60. / 3600.)), np.cos(np.radians(1.0))**2, np.radians(5. / 3600.)) pattern_list = pattern_struct.ref_candidates self.assertGreater(len(pattern_list), 0) self.assertEqual(pattern_list[0], 0) self.assertEqual(pattern_list[1], 1) self.assertEqual(pattern_list[2], 2) self.assertEqual(pattern_list[3], 3) self.assertEqual(pattern_list[4], 4) self.assertEqual(pattern_list[5], 5) pattern_struct = self.pyPPMb._construct_pattern_and_shift_rot_matrix( self.source_obj_array[:9, :3], 6, np.cos(np.radians(60. / 3600.)), np.cos(np.radians(1.0))**2, np.radians(5. / 3600.)) pattern_list = pattern_struct.ref_candidates self.assertGreater(len(pattern_list), 0) self.assertEqual(pattern_list[0], 0) self.assertEqual(pattern_list[1], 1) self.assertEqual(pattern_list[2], 2) self.assertEqual(pattern_list[3], 3) self.assertEqual(pattern_list[4], 4) self.assertEqual(pattern_list[5], 5) pattern_struct = self.pyPPMb._construct_pattern_and_shift_rot_matrix( self.source_obj_array[[2, 4, 8, 16, 32, 64], :3], 6, np.cos(np.radians(60. / 3600.)), np.cos(np.radians(1.0))**2, np.radians(5. / 3600.)) pattern_list = pattern_struct.ref_candidates self.assertEqual(pattern_list[0], 2) self.assertEqual(pattern_list[1], 4) self.assertEqual(pattern_list[2], 8) self.assertEqual(pattern_list[3], 16) self.assertEqual(pattern_list[4], 32) self.assertEqual(pattern_list[5], 64) def testMatchPerfect(self): """ Input objects that have no shift or rotation to the matcher and test that we return a match. """ self.pyPPMb = PessimisticPatternMatcherB( reference_array=self.reference_obj_array[:, :3], log=self.log) match_struct = self.pyPPMb.match(source_array=self.source_obj_array, n_check=9, n_match=6, n_agree=2, max_n_patterns=100, max_shift=60., max_rotation=5.0, max_dist=5., min_matches=30, pattern_skip_array=None) self.assertEqual(len(match_struct.match_ids), len(self.reference_obj_array)) self.assertTrue( np.all( match_struct.distances_rad < 0.01 / 3600.0 * __deg_to_rad__)) def testOptimisticMatch(self): """ Test the optimistic mode of the pattern matcher. That is the algorithm with the early exit strategy as described in Tabur 2007. """ self.pyPPMb = PessimisticPatternMatcherB( reference_array=self.reference_obj_array[:, :3], log=self.log) match_struct = self.pyPPMb.match(source_array=self.source_obj_array, n_check=9, n_match=6, n_agree=1, max_n_patterns=100, max_shift=60., max_rotation=6.0, max_dist=5., min_matches=30, pattern_skip_array=None) self.assertEqual(len(match_struct.match_ids), len(self.reference_obj_array)) self.assertTrue( np.all( match_struct.distances_rad < 0.01 / 3600.0 * __deg_to_rad__)) def testMatchSkip(self): """ Test the ability to skip specified patterns in the matching process. """ self.pyPPMb = PessimisticPatternMatcherB( reference_array=self.reference_obj_array[:, :3], log=self.log) match_struct = self.pyPPMb.match(source_array=self.source_obj_array, n_check=9, n_match=6, n_agree=2, max_n_patterns=100, max_shift=60., max_rotation=5.0, max_dist=5., min_matches=30, pattern_skip_array=np.array([0])) self.assertEqual(len(match_struct.match_ids), len(self.reference_obj_array)) self.assertTrue( np.all( match_struct.distances_rad < 0.01 / 3600.0 * __deg_to_rad__)) def testMatchMoreSources(self): """ Test the case where we have more sources than references but no rotation or shift. """ self.pyPPMb = PessimisticPatternMatcherB( reference_array=self.reference_obj_array[:500, :3], log=self.log) match_struct = self.pyPPMb.match(source_array=self.source_obj_array, n_check=9, n_match=6, n_agree=2, max_n_patterns=100, max_shift=60.0, max_rotation=5.0, max_dist=5., min_matches=30, pattern_skip_array=None) self.assertEqual(len(match_struct.match_ids), len(self.reference_obj_array[:500])) self.assertTrue( np.all( match_struct.distances_rad < 0.01 / 3600.0 * __deg_to_rad__)) def testMatchMoreReferences(self): """ Test the case where we have more references than sources but no rotation or shift. """ self.pyPPMb = PessimisticPatternMatcherB( reference_array=self.reference_obj_array[:, :3], log=self.log) match_struct = self.pyPPMb.match( source_array=self.source_obj_array[:500], n_check=9, n_match=6, n_agree=2, max_n_patterns=100, max_shift=60., max_rotation=1.0, max_dist=5., min_matches=30, pattern_skip_array=None) self.assertEqual(len(match_struct.match_ids), len(self.reference_obj_array[:500])) self.assertTrue( np.all( match_struct.distances_rad < 0.01 / 3600.0 * __deg_to_rad__)) def testShift(self): """ Test the matcher when a shift is applied to the data. We say shift here as while we are rotating the unit-sphere in 3D, on our 'focal plane' this will appear as a shift. """ self.pyPPMb = PessimisticPatternMatcherB( reference_array=self.reference_obj_array[:, :3], log=self.log) theta = np.radians(45.0 / 3600.) cos_theta = np.cos(theta) sin_theta = np.sin(theta) theta_rotation = self.pyPPMb._create_spherical_rotation_matrix( np.array([0, 0, 1]), cos_theta, sin_theta) self.source_obj_array[:, :3] = np.dot( theta_rotation, self.source_obj_array[:, :3].transpose()).transpose() match_struct = self.pyPPMb.match(source_array=self.source_obj_array, n_check=9, n_match=6, n_agree=2, max_n_patterns=100, max_shift=60, max_rotation=5.0, max_dist=5., min_matches=30, pattern_skip_array=None) self.assertEqual(len(match_struct.match_ids), len(self.reference_obj_array)) self.assertTrue( np.all( match_struct.distances_rad < 0.01 / 3600.0 * __deg_to_rad__)) def testRotation(self): """ Test the matcher for when a roation is applied to the data. """ self.pyPPMb = PessimisticPatternMatcherB( reference_array=self.reference_obj_array[:, :3], log=self.log) phi = 2.5 * __deg_to_rad__ cos_phi = np.cos(phi) sin_phi = np.sin(phi) phi_rotation = self.pyPPMb._create_spherical_rotation_matrix( np.array([1, 0, 0]), cos_phi, sin_phi) self.source_obj_array[:, :3] = np.dot( phi_rotation, self.source_obj_array[:, :3].transpose()).transpose() match_struct = self.pyPPMb.match(source_array=self.source_obj_array, n_check=9, n_match=6, n_agree=2, max_n_patterns=100, max_shift=60, max_rotation=5.0, max_dist=5., min_matches=30, pattern_skip_array=None) self.assertEqual(len(match_struct.match_ids), len(self.reference_obj_array)) self.assertTrue( np.all( match_struct.distances_rad < 0.01 / 3600.0 * __deg_to_rad__)) def testShiftRotation(self): """ Test both a shift and rotation being applied to the data. """ self.pyPPMb = PessimisticPatternMatcherB( reference_array=self.reference_obj_array[:, :3], log=self.log) theta = np.radians(45.0 / 3600.) cos_theta = np.cos(theta) sin_theta = np.sin(theta) theta_rotation = self.pyPPMb._create_spherical_rotation_matrix( np.array([0, 0, 1]), cos_theta, sin_theta) phi = 2.5 * __deg_to_rad__ cos_phi = np.cos(phi) sin_phi = np.sin(phi) phi_rotation = self.pyPPMb._create_spherical_rotation_matrix( np.array([1, 0, 0]), cos_phi, sin_phi) shift_rot_matrix = np.dot(theta_rotation, phi_rotation) self.source_obj_array[:, :3] = np.dot( shift_rot_matrix, self.source_obj_array[:, :3].transpose()).transpose() match_struct = self.pyPPMb.match(source_array=self.source_obj_array, n_check=9, n_match=6, n_agree=2, max_n_patterns=100, max_shift=60., max_rotation=5.0, max_dist=5., min_matches=30, pattern_skip_array=None) self.assertEqual(len(match_struct.match_ids), len(self.reference_obj_array)) self.assertTrue( np.all( match_struct.distances_rad < 0.01 / 3600.0 * __deg_to_rad__)) def testLinearDistortion(self): """ Create a simple linear distortion and test that the correct references are still matched. """ self.pyPPMb = PessimisticPatternMatcherB( reference_array=self.reference_obj_array[:, :3], log=self.log) max_z = np.cos(np.pi / 2 + 0.5 * __deg_to_rad__) min_z = np.cos(np.pi / 2 - 0.5 * __deg_to_rad__) # The max shift in position we add to the position will be 25 # arcseconds. max_distort = 25.0 / 3600. * __deg_to_rad__ self.source_obj_array[:, 2] = (self.source_obj_array[:, 2] - max_distort * (self.source_obj_array[:, 2] - min_z) / (max_z - min_z)) # Renomalize the 3 vectors to be unit length. distorted_dists = np.sqrt(self.source_obj_array[:, 0]**2 + self.source_obj_array[:, 1]**2 + self.source_obj_array[:, 2]**2) self.source_obj_array[:, 0] /= distorted_dists self.source_obj_array[:, 1] /= distorted_dists self.source_obj_array[:, 2] /= distorted_dists match_struct = self.pyPPMb.match(source_array=self.source_obj_array, n_check=9, n_match=6, n_agree=2, max_n_patterns=100, max_shift=60., max_rotation=5.0, max_dist=5., min_matches=30, pattern_skip_array=None) self.assertEqual(len(match_struct.match_ids), len(self.reference_obj_array)) self.assertTrue( np.all(match_struct.distances_rad < 10 / 3600.0 * __deg_to_rad__)) def testNoReferenceSources(self): """Check that we get a helpful error when no reference objects are supplied. """ with self.assertRaisesRegex(ValueError, "No reference objects supplied"): PessimisticPatternMatcherB(np.ndarray((0, 3)), self.log)