def sym_toeplitz_derivative_quadratic_form(left_vector, right_vector): """ Given a left vector v1 and a right vector v2, computes the quadratic form: v1'*(dT/dc_i)*v2 for all i, where dT/dc_i is the derivative of the Toeplitz matrix with respect to the ith element of its first column. Note that dT/dc_i is the same for any symmetric Toeplitz matrix T, so we do not require it as an argument. In particular, dT/dc_i is given by: [0 0; I_{m-i+1} 0] + [0 I_{m-i+1}; 0 0] where I_{m-i+1} is the (m-i+1) dimensional identity matrix. In other words, dT/dc_i for i=1..m is the matrix with ones on the ith sub- and superdiagonal. Args: - left_vector (vector m) - left vector v1 in the quadratic form. - right_vector (vector m) - right vector v2 in the quadratic form. Returns: - vector m - a vector so that the ith element is the result of v1'*(dT/dc_i)*v2 """ m = len(left_vector) dT_dc_col = torch.zeros(m) dT_dc_row = left_vector dT_dc_col[0] = dT_dc_row[0] res = toeplitz_mv(dT_dc_col, dT_dc_row, right_vector) dT_dc_row = utils.reverse(left_vector) dT_dc_col[0] = dT_dc_row[0] res = res + toeplitz_mv(dT_dc_col, dT_dc_row, utils.reverse(right_vector)) res[0] -= left_vector.dot(right_vector) return res
def sym_toeplitz_derivative_quadratic_form(left_vectors, right_vectors): """ Given a left vector v1 and a right vector v2, computes the quadratic form: v1'*(dT/dc_i)*v2 for all i, where dT/dc_i is the derivative of the Toeplitz matrix with respect to the ith element of its first column. Note that dT/dc_i is the same for any symmetric Toeplitz matrix T, so we do not require it as an argument. In particular, dT/dc_i is given by: [0 0; I_{m-i+1} 0] + [0 I_{m-i+1}; 0 0] where I_{m-i+1} is the (m-i+1) dimensional identity matrix. In other words, dT/dc_i for i=1..m is the matrix with ones on the ith sub- and superdiagonal. Args: - left_vectors (vector m or matrix s x m) - s left vectors u[j] in the quadratic form. - right_vectors (vector m or matrix s x m) - s right vectors v[j] in the quadratic form. Returns: - vector m - a vector so that the ith element is the result of \sum_j(u[j]*(dT/dc_i)*v[j]) """ if left_vectors.ndimension() == 1: left_vectors = left_vectors.unsqueeze(0) right_vectors = right_vectors.unsqueeze(0) if left_vectors.ndimension() == 3: batch = True batch_size, s, _ = left_vectors.size() left_vectors = left_vectors.contiguous().view(batch_size * s, -1) right_vectors = right_vectors.contiguous().view(batch_size * s, -1) else: batch = False s, m = left_vectors.size() left_vectors.contiguous() right_vectors.contiguous() columns = left_vectors.new(s, m).fill_(0) columns[:, 0] = left_vectors[:, 0] res = toeplitz_matmul(columns, left_vectors, right_vectors) rows = utils.reverse(left_vectors, dim=1) columns[:, 0] = rows[:, 0] res += toeplitz_matmul(columns, rows, utils.reverse(right_vectors, dim=1)) if not batch: res = res.sum(0) res[0] -= (left_vectors * right_vectors).sum() else: res = res.contiguous().view(batch_size, -1, m).sum(1) res[:, 0] -= (left_vectors * right_vectors).view(batch_size, -1).sum(1) return res
def test_reverse(): input = torch.Tensor([ [1, 2, 3], [4, 5, 6], ]) res = torch.Tensor([ [3, 2, 1], [6, 5, 4], ]) assert torch.equal(utils.reverse(input, dim=1), res)
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 circulant_transpose(input_column): output_column = input_column.new().resize_as_(input_column) output_column[0] = input_column[0] output_column[1:] = utils.reverse(input_column[1:]) return output_column
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
def test_reverse(self): input = torch.Tensor([[1, 2, 3], [4, 5, 6]]) res = torch.Tensor([[3, 2, 1], [6, 5, 4]]) self.assertTrue(torch.equal(utils.reverse(input, dim=1), res))