Exemple #1
0
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
Exemple #2
0
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)
Exemple #4
0
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
Exemple #5
0
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
Exemple #6
0
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
Exemple #7
0
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
Exemple #8
0
 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))