<SYSTEM_TASK:>
Solve the following problem using Python, implementing the functions described below, one line at a time
<END_TASK>
<USER_TASK:>
Description:
 def match(self, source_points, target_points, source_normals, target_normals):
 """ Matches points between two point-normal sets. Uses the closest ip to choose matches, with distance for thresholding only. Parameters source_point_cloud : Nx3 :obj:`numpy.ndarray` source object points target_point_cloud : Nx3 :obj:`numpy.ndarray` target object points source_normal_cloud : Nx3 :obj:`numpy.ndarray` source object outward-pointing normals target_normal_cloud : Nx3 :obj`numpy.ndarray` target object outward-pointing normals Returns ------- :obj`Correspondences` the correspondences between source and target """

        # compute the distances and inner products between the point sets
        dists = ssd.cdist(source_points, target_points, 'euclidean')
        ip = source_normals.dot(target_normals.T) # abs because we don't have correct orientations
        source_ip = source_points.dot(target_normals.T)
        target_ip = target_points.dot(target_normals.T)
        target_ip = np.diag(target_ip)
        target_ip = np.tile(target_ip, [source_points.shape[0], 1])
        abs_diff = np.abs(source_ip - target_ip) # difference in inner products

        # mark invalid correspondences
        invalid_dists = np.where(dists > self.dist_thresh_)
        abs_diff[invalid_dists[0], invalid_dists[1]] = np.inf
        invalid_norms = np.where(ip < self.norm_thresh_)
        abs_diff[invalid_norms[0], invalid_norms[1]] = np.inf

        # choose the closest matches
        match_indices = np.argmin(abs_diff, axis=1)
        match_vals = np.min(abs_diff, axis=1)
        invalid_matches = np.where(match_vals == np.inf)
        match_indices[invalid_matches[0]] = -1

        return NormalCorrespondences(match_indices, source_points, target_points, source_normals, target_normals)