def test_ifft1_returns_type_of_original_input(self): d = 8 input = torch.randn(6, d) res = fft.fft1(input).double() recon = fft.ifft1(res) self.assertEqual(input.size(), recon.size()) self.assertLess(torch.norm(input.double() - recon), 1e-5) self.assertTrue(isinstance(res, torch.DoubleTensor))
def test_ifft1_computes_ifft_of_1d_input_with_odd_size(self): d = 9 input = torch.randn(d) res = fft.fft1(input) recon = fft.ifft1(res) self.assertEqual(input.size(), recon.size()) self.assertLess(torch.norm(input - recon), 1e-5)
def test_ifft1_computes_ifft_of_2d_input(self): d = 8 input = torch.randn(6, d) res = fft.fft1(input) recon = fft.ifft1(res) self.assertEqual(input.size(), recon.size()) self.assertLess(torch.norm(input - recon), 1e-5)
def test_ifft1_returns_type_of_original_input(): d = 8 input = torch.randn(6, d) res = fft.fft1(input).double() recon = fft.ifft1(res) assert input.size() == recon.size() assert torch.norm(input.double() - recon) < 1e-5 assert isinstance(res, torch.DoubleTensor)
def test_ifft1_computes_ifft_of_2d_input(): d = 8 input = torch.randn(6, d) res = fft.fft1(input) recon = fft.ifft1(res) assert input.size() == recon.size() assert torch.norm(input - recon) < 1e-5
def test_ifft1_computes_ifft_of_1d_input_with_odd_size(): d = 9 input = torch.randn(d) res = fft.fft1(input) recon = fft.ifft1(res, input.size()) assert input.size() == recon.size() assert torch.norm(input - recon) < 1e-5
def circulant_matmul(circulant_column, tensor): """ Performs a matrix multiplication CM where the tensor C is circulant. Args: - circulant_column (vector n) - First column of the circulant tensor C. - tensor (tensor n x p) - Matrix or vector to multiply the Toeplitz tensor with. Returns: - tensor """ if circulant_column.ndimension() != 1 or tensor.ndimension() != 2: raise RuntimeError( 'All inputs to CirculantMV should be vectors (first column c and row r of the Toeplitz \ tensor plus the target vector vector).') if len(circulant_column) != len(tensor): raise RuntimeError( 'c and r should have the same length (Toeplitz matrices are necessarily square).' ) if type(circulant_column) != type(tensor): raise RuntimeError('The types of all inputs to ToeplitzMV must match.') output_dims = tensor.ndimension() if output_dims == 1: tensor = tensor.unsqueeze(1) if len(circulant_column) == 1: output = (circulant_column.view(1, 1).mv(tensor)) else: fft_M = fft.fft1(tensor.t().contiguous()) fft_c = fft.fft1(circulant_column).expand_as(fft_M) fft_product = fft_M.new(*fft_M.size()).zero_() fft_product[:, :, 0].addcmul_(fft_c[:, :, 0], fft_M[:, :, 0]) fft_product[:, :, 0].addcmul_(-1, fft_c[:, :, 1], fft_M[:, :, 1]) fft_product[:, :, 1].addcmul_(fft_c[:, :, 1], fft_M[:, :, 0]) fft_product[:, :, 1].addcmul_(fft_c[:, :, 0], fft_M[:, :, 1]) output = fft.ifft1(fft_product, tensor.size()).t() if output_dims == 1: output = output.squeeze(1) return output
def circulant_inv_matmul(circulant_column, matrix): """ Performs a batch of linear solves C^{-1}M where the matrix C is circulant. Args: - circulant_column (vector n) - First column of the circulant matrix C. - matrix (matrix n x p) - Matrix to multiply the Toeplitz matrix with. Returns: - Matrix (n x p) - The result of the linear solves C^{-1}M. """ if circulant_column.ndimension() != 1: raise RuntimeError( 'All inputs to CirculantMatmul should be vectors (first column c and row r of the Toeplitz \ matrix plus the target vector vector).') if len(circulant_column) != len(matrix): raise RuntimeError( 'c and r should have the same length (Toeplitz matrices are necessarily square).' ) if type(circulant_column) != type(matrix): raise RuntimeError('The types of all inputs to ToeplitzMV must match.') if len(circulant_column) == 1: return (circulant_column.view(1, 1).mv(matrix)) fft_M = fft.fft1(matrix.t().contiguous()) fft_c = fft.fft1(circulant_column) denominator = fft_c[:, 0].pow(2) + fft_c[:, 1].pow(2) fft_c[:, 0] = fft_c[:, 0] / denominator fft_c[:, 1] = -fft_c[:, 1] / denominator fft_c = fft_c.expand_as(fft_M) fft_product = fft_M.new(*fft_M.size()).zero_() fft_product[:, :, 0].addcmul_(fft_c[:, :, 0], fft_M[:, :, 0]) fft_product[:, :, 0].addcmul_(-1, fft_c[:, :, 1], fft_M[:, :, 1]) fft_product[:, :, 1].addcmul_(fft_c[:, :, 1], fft_M[:, :, 0]) fft_product[:, :, 1].addcmul_(fft_c[:, :, 0], fft_M[:, :, 1]) res = fft.ifft1(fft_product, matrix.size()).t() return res
def test_fft1_computes_fft_of_nd_input(self): d = 8 input = torch.randn(3, 6, d) res = fft.fft1(input) actual = np.fft.fft(input.numpy()) self.assertEqual(tuple(res.size()), (3, 6, 8, 2)) res_real = res[:, :, :, 0] res_imag = res[:, :, :, 1] actual_real = torch.from_numpy(actual.real[:, :, :]).float() actual_imag = torch.from_numpy(actual.imag[:, :, :]).float() self.assertLess(torch.norm(res_real - actual_real), 1e-5) self.assertLess(torch.norm(res_imag - actual_imag), 1e-5)
def test_fft1_computes_fft_of_1d_input(self): d = 8 input = torch.randn(d) res = fft.fft1(input) actual = np.fft.fft(input.numpy()) self.assertEqual(tuple(res.size()), (8, 2)) res_real = res[:, 0] res_imag = res[:, 1] actual_real = torch.from_numpy(actual.real).float() actual_imag = torch.from_numpy(actual.imag).float() assert torch.norm(res_real - actual_real) < 1e-5 assert torch.norm(res_imag - actual_imag) < 1e-5
def test_fft1_computes_fft_of_nd_input(): d = 8 input = torch.randn(3, 6, d) res = fft.fft1(input) actual = np.fft.fft(input.numpy()) assert (tuple(res.size()) == (3, 6, 5, 2)) res_real = res[:, :, :, 0] res_imag = res[:, :, :, 1] actual_real = torch.from_numpy(actual.real[:, :, :5]).float() actual_imag = torch.from_numpy(actual.imag[:, :, :5]).float() assert torch.norm(res_real - actual_real) < 1e-5 assert torch.norm(res_imag - actual_imag) < 1e-5
def toeplitz_matmul(toeplitz_column, toeplitz_row, tensor): """ Performs multiplication T * M where the matrix T is Toeplitz. Args: - toeplitz_column (vector n or b x n) - First column of the Toeplitz matrix T. - toeplitz_row (vector n or b x n) - First row of the Toeplitz matrix T. - tensor (matrix n x p or b x n x p) - Matrix or vector to multiply the Toeplitz matrix with. Returns: - tensor (n x p or b x n x p) - The result of the matrix multiply T * M. """ if toeplitz_column.size() != toeplitz_row.size(): raise RuntimeError( 'c and r should have the same length (Toeplitz matrices are necessarily square).' ) is_batch = True if toeplitz_column.ndimension() == 1: toeplitz_column = toeplitz_column.unsqueeze(0) toeplitz_row = toeplitz_row.unsqueeze(0) if tensor.ndimension() < 3: tensor = tensor.unsqueeze(0) is_batch = False if toeplitz_column.ndimension() != 2: raise RuntimeError( 'The first two inputs to ToeplitzMV should be vectors \ (or matrices, representing batch) \ (first column c and row r of the Toeplitz matrix)') if toeplitz_column.size(0) == 1: toeplitz_column = toeplitz_column.expand(tensor.size(0), toeplitz_column.size(1)) toeplitz_row = toeplitz_row.expand(tensor.size(0), toeplitz_row.size(1)) if toeplitz_column.size()[:2] != tensor.size()[:2]: raise RuntimeError( 'Dimension mismatch: attempting to multiply a {}x{} Toeplitz matrix against a matrix with \ leading dimension {}.'.format( len(toeplitz_column), len(toeplitz_column), len(tensor))) if not torch.equal(toeplitz_column[:, 0], toeplitz_row[:, 0]): raise RuntimeError( 'The first column and first row of the Toeplitz matrix should have the same first element, \ otherwise the value of T[0,0] is ambiguous. \ Got: c[0]={} and r[0]={}'.format( toeplitz_column[0], toeplitz_row[0])) if type(toeplitz_column) != type(toeplitz_row) or type( toeplitz_column) != type(tensor): raise RuntimeError('The types of all inputs to ToeplitzMV must match.') output_dims = tensor.ndimension() if output_dims == 2: tensor = tensor.unsqueeze(2) if toeplitz_column.size(1) == 1: output = toeplitz_column.view(-1, 1, 1).matmul(tensor) else: batch_size, orig_size, num_rhs = tensor.size() r_reverse = utils.reverse(toeplitz_row[:, 1:], dim=1) c_r_rev = toeplitz_column.new(batch_size, orig_size + r_reverse.size(1)).zero_() c_r_rev[:, :orig_size] = toeplitz_column c_r_rev[:, orig_size:] = r_reverse temp_tensor = toeplitz_column.new(batch_size, 2 * orig_size - 1, num_rhs).zero_() temp_tensor[:, :orig_size, :] = tensor fft_M = fft.fft1(temp_tensor.transpose(1, 2).contiguous()) fft_c = fft.fft1(c_r_rev).unsqueeze(1).expand_as(fft_M) fft_product = toeplitz_column.new(fft_M.size()).zero_() fft_product[:, :, :, 0].addcmul_(fft_c[:, :, :, 0], fft_M[:, :, :, 0]) fft_product[:, :, :, 0].addcmul_(-1, fft_c[:, :, :, 1], fft_M[:, :, :, 1]) fft_product[:, :, :, 1].addcmul_(fft_c[:, :, :, 1], fft_M[:, :, :, 0]) fft_product[:, :, :, 1].addcmul_(fft_c[:, :, :, 0], fft_M[:, :, :, 1]) output = fft.ifft1(fft_product, (batch_size, num_rhs, 2 * orig_size - 1)).transpose( 1, 2) output = output[:, :orig_size, :] if output_dims == 2: output = output.squeeze(2) if not is_batch: output = output.squeeze(0) return output
def test_fft1_returns_type_of_original_input(self): d = 8 input = torch.randn(3, 6, d).double() res = fft.fft1(input) self.assertTrue(isinstance(res, torch.DoubleTensor))
def toeplitz_mv(toeplitz_column, toeplitz_row, vector): """ Performs a matrix-vector multiplication Tv where the matrix T is Toeplitz. Args: - toeplitz_column (vector n) - First column of the Toeplitz matrix T. - toeplitz_row (vector n) - First row of the Toeplitz matrix T. - vector (vector n) - Vector to multiply the Toeplitz matrix with. Returns: - vector n - The result of the matrix-vector multiply Tv. """ if toeplitz_column.ndimension() != 1 or toeplitz_row.ndimension( ) != 1 or vector.ndimension() != 1: raise RuntimeError( 'All inputs to ToeplitzMV should be vectors (first column c and row r of the Toeplitz \ matrix plus the target vector vector).') if len(toeplitz_column) != len(toeplitz_row): raise RuntimeError( 'c and r should have the same length (Toeplitz matrices are necessarily square).' ) if len(toeplitz_column) != len(vector): raise RuntimeError( 'Dimension mismatch: attempting to multiply a {}x{} Toeplitz matrix against a length \ {} vector.'.format(len(toeplitz_column), len(toeplitz_column), len(vector))) if toeplitz_column[0] != toeplitz_row[0]: raise RuntimeError( 'The first column and first row of the Toeplitz matrix should have the same first \ otherwise the value of T[0,0] is ambiguous. \ Got: c[0]={} and r[0]={}'.format( toeplitz_column[0], toeplitz_row[0])) if type(toeplitz_column) != type(toeplitz_row) or type( toeplitz_column) != type(vector): raise RuntimeError('The types of all inputs to ToeplitzMV must match.') if len(toeplitz_column) == 1: return (toeplitz_column.view(1, 1).mv(vector)) orig_size = len(toeplitz_column) r_reverse = utils.reverse(toeplitz_row[1:]) c_r_rev = torch.zeros(orig_size + len(r_reverse)) c_r_rev[:orig_size] = toeplitz_column c_r_rev[orig_size:] = r_reverse temp_vector = torch.zeros(2 * orig_size - 1) temp_vector[:orig_size] = vector fft_c = fft.fft1(c_r_rev) fft_v = fft.fft1(temp_vector) fft_product = torch.zeros(fft_c.size()) fft_product[:, 0].addcmul_(fft_c[:, 0], fft_v[:, 0]) fft_product[:, 0].addcmul_(-1, fft_c[:, 1], fft_v[:, 1]) fft_product[:, 1].addcmul_(fft_c[:, 1], fft_v[:, 0]) fft_product[:, 1].addcmul_(fft_c[:, 0], fft_v[:, 1]) res = fft.ifft1(fft_product, temp_vector.size()) res.resize_(orig_size) return res
def toeplitz_mm(toeplitz_column, toeplitz_row, matrix): """ Performs a matrix-matrix multiplication TM where the matrix T is Toeplitz. Args: - toeplitz_column (vector n) - First column of the Toeplitz matrix T. - toeplitz_row (vector n) - First row of the Toeplitz matrix T. - matrix (matrix n x p) - Matrix to multiply the Toeplitz matrix with. Returns: - Matrix (n x p) - The result of the matrix-vector multiply TM. """ if toeplitz_column.ndimension() != 1 or toeplitz_row.ndimension( ) != 1 or matrix.ndimension() != 2: raise RuntimeError( 'The first two inputs to ToeplitzMV should be vectors \ (first column c and row r of the Toeplitz matrix), and the last input should be a matrix.' ) if len(toeplitz_column) != len(toeplitz_row): raise RuntimeError( 'c and r should have the same length (Toeplitz matrices are necessarily square).' ) if len(toeplitz_column) != len(matrix): raise RuntimeError( 'Dimension mismatch: attempting to multiply a {}x{} Toeplitz matrix against a matrix with \ leading dimension {}.'.format( len(toeplitz_column), len(toeplitz_column), len(matrix))) if toeplitz_column[0] != toeplitz_row[0]: raise RuntimeError( 'The first column and first row of the Toeplitz matrix should have the same first element, \ otherwise the value of T[0,0] is ambiguous. \ Got: c[0]={} and r[0]={}'.format( toeplitz_column[0], toeplitz_row[0])) if type(toeplitz_column) != type(toeplitz_row) or type( toeplitz_column) != type(matrix): raise RuntimeError('The types of all inputs to ToeplitzMV must match.') if len(toeplitz_column) == 1: return (toeplitz_column.view(1, 1).mm(matrix)) _, num_rhs = matrix.size() orig_size = len(toeplitz_column) r_reverse = utils.reverse(toeplitz_row[1:]) c_r_rev = torch.zeros(orig_size + len(r_reverse)) c_r_rev[:orig_size] = toeplitz_column c_r_rev[orig_size:] = r_reverse temp_matrix = torch.zeros(2 * orig_size - 1, num_rhs) temp_matrix[:orig_size, :] = matrix fft_M = fft.fft1(temp_matrix.t().contiguous()) fft_c = fft.fft1(c_r_rev).expand_as(fft_M) fft_product = torch.zeros(fft_M.size()) fft_product[:, :, 0].addcmul_(fft_c[:, :, 0], fft_M[:, :, 0]) fft_product[:, :, 0].addcmul_(-1, fft_c[:, :, 1], fft_M[:, :, 1]) fft_product[:, :, 1].addcmul_(fft_c[:, :, 1], fft_M[:, :, 0]) fft_product[:, :, 1].addcmul_(fft_c[:, :, 0], fft_M[:, :, 1]) res = fft.ifft1(fft_product, (num_rhs, 2 * orig_size - 1)).t() res = res[:orig_size, :] return res