def _matmul(self, x, adjoint=False, adjoint_arg=False): # Given a Toeplitz matrix, we can embed it in a Circulant matrix to perform # efficient matrix multiplications. Given a Toeplitz matrix with first row # [t_0, t_1, ... t_{n-1}] and first column [t0, t_{-1}, ..., t_{-(n-1)}, # let C by the circulant matrix with first column [t0, t_{-1}, ..., # t_{-(n-1)}, 0, t_{n-1}, ..., t_1]. Also adjoin to our input vector `x` # `n` zeros, to make it a vector of length `2n` (call it y). It can be shown # that if we take the first n entries of `Cy`, this is equal to the Toeplitz # multiplication. See: # http://math.mit.edu/icg/resources/teaching/18.085-spring2015/toeplitz.pdf # for more details. x = linalg.adjoint(x) if adjoint_arg else x expanded_x = array_ops.concat([x, array_ops.zeros_like(x)], axis=-2) col = ops.convert_to_tensor(self.col) row = ops.convert_to_tensor(self.row) circulant_col = array_ops.concat([ col, array_ops.zeros_like(col[..., 0:1]), array_ops.reverse(row[..., 1:], axis=[-1]) ], axis=-1) circulant = linear_operator_circulant.LinearOperatorCirculant( fft_ops.fft(_to_complex(circulant_col)), input_output_dtype=row.dtype) result = circulant.matmul(expanded_x, adjoint=adjoint, adjoint_arg=False) shape = self._shape_tensor(row=row, col=col) return math_ops.cast( result[..., :self._domain_dimension_tensor(shape=shape), :], self.dtype)
def _inverse_circulant(circulant_operator): # Inverting the spectrum is sufficient to get the inverse. return linear_operator_circulant.LinearOperatorCirculant( spectrum=1. / circulant_operator.spectrum, is_non_singular=circulant_operator.is_non_singular, is_self_adjoint=circulant_operator.is_self_adjoint, is_positive_definite=circulant_operator.is_positive_definite, is_square=True)
def _matmul_linear_operator_circulant_circulant(linop_a, linop_b): return linear_operator_circulant.LinearOperatorCirculant( spectrum=linop_a.spectrum * linop_b.spectrum, is_non_singular=_combined_non_singular_hint(linop_a, linop_b), is_self_adjoint=_combined_self_adjoint_hint(linop_a, linop_b), is_positive_definite=_combined_positive_definite_hint( linop_a, linop_b), is_square=True)
def __init__(self, col, row, is_non_singular=None, is_self_adjoint=None, is_positive_definite=None, is_square=None, name="LinearOperatorToeplitz"): r"""Initialize a `LinearOperatorToeplitz`. Args: col: Shape `[B1,...,Bb, N]` `Tensor` with `b >= 0` `N >= 0`. The first column of the operator. Allowed dtypes: `float16`, `float32`, `float64`, `complex64`, `complex128`. Note that the first entry of `col` is assumed to be the same as the first entry of `row`. row: Shape `[B1,...,Bb, N]` `Tensor` with `b >= 0` `N >= 0`. The first row of the operator. Allowed dtypes: `float16`, `float32`, `float64`, `complex64`, `complex128`. Note that the first entry of `row` is assumed to be the same as the first entry of `col`. is_non_singular: Expect that this operator is non-singular. is_self_adjoint: Expect that this operator is equal to its hermitian transpose. If `diag.dtype` is real, this is auto-set to `True`. is_positive_definite: Expect that this operator is positive definite, meaning the quadratic form `x^H A x` has positive real part for all nonzero `x`. Note that we do not require the operator to be self-adjoint to be positive-definite. See: https://en.wikipedia.org/wiki/Positive-definite_matrix#Extension_for_non-symmetric_matrices is_square: Expect that this operator acts like square [batch] matrices. name: A name for this `LinearOperator`. """ with ops.name_scope(name, values=[row, col]): self._row = ops.convert_to_tensor(row, name="row") self._col = ops.convert_to_tensor(col, name="col") self._check_row_col(self._row, self._col) circulant_col = array_ops.concat( [self._col, array_ops.zeros_like(self._col[..., 0:1]), array_ops.reverse(self._row[..., 1:], axis=[-1])], axis=-1) # To be used for matmul. self._circulant = linear_operator_circulant.LinearOperatorCirculant( fft_ops.fft(_to_complex(circulant_col)), input_output_dtype=self._row.dtype) if is_square is False: # pylint:disable=g-bool-id-comparison raise ValueError("Only square Toeplitz operators currently supported.") is_square = True super(LinearOperatorToeplitz, self).__init__( dtype=self._row.dtype, graph_parents=[self._row, self._col], is_non_singular=is_non_singular, is_self_adjoint=is_self_adjoint, is_positive_definite=is_positive_definite, is_square=is_square, name=name)
def _solve_linear_operator_circulant_circulant(linop_a, linop_b): return linear_operator_circulant.LinearOperatorCirculant( spectrum=linop_b.spectrum / linop_a.spectrum, is_non_singular=registrations_util.combined_non_singular_hint( linop_a, linop_b), is_self_adjoint=registrations_util. combined_commuting_self_adjoint_hint(linop_a, linop_b), is_positive_definite=( registrations_util.combined_commuting_positive_definite_hint( linop_a, linop_b)), is_square=True)
def _adjoint_circulant(circulant_operator): spectrum = circulant_operator.spectrum if spectrum.dtype.is_complex: spectrum = math_ops.conj(spectrum) # Conjugating the spectrum is sufficient to get the adjoint. return linear_operator_circulant.LinearOperatorCirculant( spectrum=spectrum, is_non_singular=circulant_operator.is_non_singular, is_self_adjoint=circulant_operator.is_self_adjoint, is_positive_definite=circulant_operator.is_positive_definite, is_square=True)
def _operator_from_kernel(kernel, d, **kwargs): spectrum = linear_operator_circulant._FFT_OP[d](math_ops.cast( kernel, dtypes.complex64)) if d == 1: return linear_operator_circulant.LinearOperatorCirculant( spectrum, **kwargs) elif d == 2: return linear_operator_circulant.LinearOperatorCirculant2D( spectrum, **kwargs) elif d == 3: return linear_operator_circulant.LinearOperatorCirculant3D( spectrum, **kwargs)