r"""Computes the matrix exponential of one or more square matrices. exp(A) = \sum_{n=0}^\infty A^n/n! The exponential is computed using a combination of the scaling and squaring method and the Pade approximation. Details can be found in: Nicholas J. Higham, "The scaling and squaring method
(input, name=None)
| 230 | |
| 231 | @tf_export('linalg.expm') |
| 232 | def matrix_exponential(input, name=None): # pylint: disable=redefined-builtin |
| 233 | r"""Computes the matrix exponential of one or more square matrices. |
| 234 | |
| 235 | exp(A) = \sum_{n=0}^\infty A^n/n! |
| 236 | |
| 237 | The exponential is computed using a combination of the scaling and squaring |
| 238 | method and the Pade approximation. Details can be found in: |
| 239 | Nicholas J. Higham, "The scaling and squaring method for the matrix |
| 240 | exponential revisited," SIAM J. Matrix Anal. Applic., 26:1179-1193, 2005. |
| 241 | |
| 242 | The input is a tensor of shape `[..., M, M]` whose inner-most 2 dimensions |
| 243 | form square matrices. The output is a tensor of the same shape as the input |
| 244 | containing the exponential for all input submatrices `[..., :, :]`. |
| 245 | |
| 246 | Args: |
| 247 | input: A `Tensor`. Must be `float16`, `float32`, `float64`, `complex64`, or |
| 248 | `complex128` with shape `[..., M, M]`. |
| 249 | name: A name to give this `Op` (optional). |
| 250 | |
| 251 | Returns: |
| 252 | the matrix exponential of the input. |
| 253 | |
| 254 | Raises: |
| 255 | ValueError: An unsupported type is provided as input. |
| 256 | |
| 257 | @compatibility(scipy) |
| 258 | Equivalent to scipy.linalg.expm |
| 259 | @end_compatibility |
| 260 | """ |
| 261 | with ops.name_scope(name, 'matrix_exponential', [input]): |
| 262 | matrix = ops.convert_to_tensor(input, name='input') |
| 263 | if matrix.shape[-2:] == [0, 0]: |
| 264 | return matrix |
| 265 | batch_shape = matrix.shape[:-2] |
| 266 | if not batch_shape.is_fully_defined(): |
| 267 | batch_shape = array_ops.shape(matrix)[:-2] |
| 268 | |
| 269 | # reshaping the batch makes the where statements work better |
| 270 | matrix = array_ops.reshape( |
| 271 | matrix, array_ops.concat(([-1], array_ops.shape(matrix)[-2:]), axis=0)) |
| 272 | l1_norm = math_ops.reduce_max( |
| 273 | math_ops.reduce_sum( |
| 274 | math_ops.abs(matrix), |
| 275 | axis=array_ops.size(array_ops.shape(matrix)) - 2), |
| 276 | axis=-1) |
| 277 | const = lambda x: constant_op.constant(x, l1_norm.dtype) |
| 278 | |
| 279 | def _nest_where(vals, cases): |
| 280 | assert len(vals) == len(cases) - 1 |
| 281 | if len(vals) == 1: |
| 282 | return array_ops.where( |
| 283 | math_ops.less(l1_norm, const(vals[0])), cases[0], cases[1]) |
| 284 | else: |
| 285 | return array_ops.where( |
| 286 | math_ops.less(l1_norm, const(vals[0])), cases[0], |
| 287 | _nest_where(vals[1:], cases[1:])) |
| 288 | |
| 289 | if matrix.dtype in [dtypes.float16, dtypes.float32, dtypes.complex64]: |
nothing calls this directly
no test coverage detected