From e10ec20a746d77d05ca79cc6dce60890bd7bc903 Mon Sep 17 00:00:00 2001 From: Evgenii Zheltonozhskii Date: Thu, 2 May 2019 19:45:19 +0300 Subject: [PATCH 01/11] Initial implementation of general eigenvalue decomposition --- tensorflow/compiler/tests/BUILD | 18 ++ tensorflow/core/kernels/BUILD | 10 + tensorflow/core/kernels/eig_op_complex128.cc | 23 +++ tensorflow/core/kernels/eig_op_complex64.cc | 23 +++ tensorflow/core/kernels/eig_op_impl.h | 92 +++++++++ tensorflow/core/ops/linalg_ops.cc | 8 + tensorflow/core/ops/ops.pbtxt | 32 +++ tensorflow/python/kernel_tests/BUILD | 21 ++ tensorflow/python/kernel_tests/eig_op_test.py | 190 ++++++++++++++++++ .../kernel_tests/self_adjoint_eig_op_test.py | 2 +- tensorflow/python/ops/linalg_ops.py | 43 ++++ .../api/golden/v1/tensorflow.raw_ops.pbtxt | 4 + .../api/golden/v2/tensorflow.raw_ops.pbtxt | 4 + 13 files changed, 469 insertions(+), 1 deletion(-) create mode 100644 tensorflow/core/kernels/eig_op_complex128.cc create mode 100644 tensorflow/core/kernels/eig_op_complex64.cc create mode 100644 tensorflow/core/kernels/eig_op_impl.h create mode 100644 tensorflow/python/kernel_tests/eig_op_test.py diff --git a/tensorflow/compiler/tests/BUILD b/tensorflow/compiler/tests/BUILD index e4a5752efe0..511eeae6946 100644 --- a/tensorflow/compiler/tests/BUILD +++ b/tensorflow/compiler/tests/BUILD @@ -256,6 +256,24 @@ tf_xla_py_test( ], ) +tf_xla_py_test( + name = "eig_op_test", + size = "medium", + srcs = ["eig_op_test.py"], + tags = ["optonly"], + deps = [ + ":xla_test", + "//tensorflow/python:array_ops", + "//tensorflow/python:framework", + "//tensorflow/python:map_fn", + "//tensorflow/python:math_ops", + "//tensorflow/python:platform_test", + "//tensorflow/python:training", + "@absl_py//absl/testing:parameterized", + ], +) + + tf_xla_py_test( name = "self_adjoint_eig_op_test", size = "medium", diff --git a/tensorflow/core/kernels/BUILD b/tensorflow/core/kernels/BUILD index d5607f641af..8716a97cbbe 100644 --- a/tensorflow/core/kernels/BUILD +++ b/tensorflow/core/kernels/BUILD @@ -3373,6 +3373,7 @@ cc_library( ":qr_op", ":self_adjoint_eig_op", ":self_adjoint_eig_v2_op", + ":eig_op", ":svd_op", ":tridiagonal_matmul_op", ":tridiagonal_solve_op", @@ -3473,6 +3474,15 @@ tf_kernel_library( ]), ) +tf_kernel_library( + name = "eig_op", + prefix = "eig_op", + deps = LINALG_DEPS + ["//tensorflow/core:lib_internal"] + if_cuda([ + ":cast_op", + ":cwise_op", + ]), +) + tf_kernel_library( name = "matrix_inverse_op", prefix = "matrix_inverse_op", diff --git a/tensorflow/core/kernels/eig_op_complex128.cc b/tensorflow/core/kernels/eig_op_complex128.cc new file mode 100644 index 00000000000..954b22f3def --- /dev/null +++ b/tensorflow/core/kernels/eig_op_complex128.cc @@ -0,0 +1,23 @@ +/* Copyright 2017 The TensorFlow Authors. All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +==============================================================================*/ + +#include "tensorflow/core/kernels/eig_op_impl.h" + +namespace tensorflow { + +REGISTER_LINALG_OP("Eig", (ComplexEigOp), + complex128); + +} // namespace tensorflow diff --git a/tensorflow/core/kernels/eig_op_complex64.cc b/tensorflow/core/kernels/eig_op_complex64.cc new file mode 100644 index 00000000000..6af00394687 --- /dev/null +++ b/tensorflow/core/kernels/eig_op_complex64.cc @@ -0,0 +1,23 @@ +/* Copyright 2019 The TensorFlow Authors. All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +==============================================================================*/ + +#include "tensorflow/core/kernels/eig_op_impl.h" + +namespace tensorflow { + +REGISTER_LINALG_OP("Eig", (ComplexEigOp), + complex64); + +} // namespace tensorflow diff --git a/tensorflow/core/kernels/eig_op_impl.h b/tensorflow/core/kernels/eig_op_impl.h new file mode 100644 index 00000000000..82ce7bdf5bd --- /dev/null +++ b/tensorflow/core/kernels/eig_op_impl.h @@ -0,0 +1,92 @@ +/* Copyright 2017 The TensorFlow Authors. All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +==============================================================================*/ + +#ifndef TENSORFLOW_CORE_KERNELS_EIG_OP_IMPL_H_ +#define TENSORFLOW_CORE_KERNELS_EIG_OP_IMPL_H_ + +// See docs in ../ops/linalg_ops.cc. + +#include "third_party/eigen3/Eigen/Core" +#include "third_party/eigen3/Eigen/Eigenvalues" +#include "tensorflow/core/framework/kernel_def_builder.h" +#include "tensorflow/core/framework/op_kernel.h" +#include "tensorflow/core/framework/tensor_shape.h" +#include "tensorflow/core/kernels/linalg_ops_common.h" +#include "tensorflow/core/lib/core/errors.h" +#include "tensorflow/core/platform/denormal.h" +#include "tensorflow/core/platform/logging.h" +#include "tensorflow/core/platform/types.h" + +namespace tensorflow { + +template +class ComplexEigOp : public LinearAlgebraOp { + public: + typedef LinearAlgebraOp Base; + + explicit ComplexEigOp(OpKernelConstruction* context) : Base(context) { + OP_REQUIRES_OK(context, context->GetAttr("compute_v", &compute_v_)); + } + + using TensorShapes = typename Base::TensorShapes; + using Matrix = typename Base::Matrix; + using MatrixMaps = typename Base::MatrixMaps; + using ConstMatrixMap = typename Base::ConstMatrixMap; + using ConstMatrixMaps = typename Base::ConstMatrixMaps; + + TensorShapes GetOutputMatrixShapes( + const TensorShapes& input_matrix_shapes) const final { + int64 n = input_matrix_shapes[0].dim_size(0); + if (compute_v_) { + return TensorShapes({TensorShape({n}), TensorShape({n, n})}); + } else { + return TensorShapes({TensorShape({n})}); + } + } + + void ComputeMatrix(OpKernelContext* context, const ConstMatrixMaps& inputs, + MatrixMaps* outputs) final { + const int64 rows = inputs[0].rows(); + if (rows == 0) { + // If X is an empty matrix (0 rows, 0 col), X * X' == X. + // Therefore, we return X. + return; + } + + // This algorithm relies on denormals, so switch them back on locally. + port::ScopedDontFlushDenormal dont_flush_denormals; + + Eigen::ComplexEigenSolver eig( + inputs[0], + compute_v_ ? Eigen::ComputeEigenvectors : Eigen::EigenvaluesOnly); + // TODO(rmlarsen): Output more detailed error info on failure. + OP_REQUIRES( + context, eig.info() == Eigen::Success, + errors::InvalidArgument("Eigen decomposition was not " + "successful. The input might not be valid.")); + + outputs->at(0) = eig.eigenvalues().template cast(); + if (compute_v_) { + outputs->at(1) = eig.eigenvectors(); + } + } + + private: + bool compute_v_; +}; + +} // namespace tensorflow + +#endif // TENSORFLOW_CORE_KERNELS_EIG_OP_IMPL_H_ diff --git a/tensorflow/core/ops/linalg_ops.cc b/tensorflow/core/ops/linalg_ops.cc index f037d38ef81..7ee83b2ce44 100644 --- a/tensorflow/core/ops/linalg_ops.cc +++ b/tensorflow/core/ops/linalg_ops.cc @@ -383,6 +383,14 @@ REGISTER_OP("SelfAdjointEig") return Status::OK(); }); +REGISTER_OP("Eig") + .Input("input: T") + .Output("e: T") + .Output("v: T") + .Attr("compute_v: bool = True") + .Attr("T: {complex64, complex128}") + .SetShapeFn(SelfAdjointEigV2ShapeFn); + REGISTER_OP("SelfAdjointEigV2") .Input("input: T") .Output("e: T") diff --git a/tensorflow/core/ops/ops.pbtxt b/tensorflow/core/ops/ops.pbtxt index 617634980dd..00970ca44e8 100644 --- a/tensorflow/core/ops/ops.pbtxt +++ b/tensorflow/core/ops/ops.pbtxt @@ -12206,6 +12206,38 @@ op { type: "type" } } +op { + name: "Eig" + input_arg { + name: "input" + type_attr: "T" + } + output_arg { + name: "e" + type_attr: "T" + } + output_arg { + name: "v" + type_attr: "T" + } + attr { + name: "compute_v" + type: "bool" + default_value { + b: true + } + } + attr { + name: "T" + type: "type" + allowed_values { + list { + type: DT_COMPLEX64 + type: DT_COMPLEX128 + } + } + } +} op { name: "Elu" input_arg { diff --git a/tensorflow/python/kernel_tests/BUILD b/tensorflow/python/kernel_tests/BUILD index eba090d409f..6bd88a24b20 100644 --- a/tensorflow/python/kernel_tests/BUILD +++ b/tensorflow/python/kernel_tests/BUILD @@ -3356,6 +3356,27 @@ cuda_py_test( shard_count = 20, ) +tf_py_test( + name = "eig_op_test", + size = "medium", + srcs = ["eig_op_test.py"], + additional_deps = [ + "//third_party/py/numpy", + "//tensorflow/python:array_ops", + "//tensorflow/python:client_testlib", + "//tensorflow/python:framework_for_generated_wrappers", + "//tensorflow/python:linalg_ops", + "//tensorflow/python:math_ops", + ], + data = ["//tensorflow/python/kernel_tests/testdata:self_adjoint_eig_op_test_files"], + shard_count = 20, + tags = [ + "no_rocm", # flaky test + "no_windows", + ], + # b/127344411: xla_enable_strict_auto_jit = True, +) + cuda_py_test( name = "self_adjoint_eig_op_test", size = "medium", diff --git a/tensorflow/python/kernel_tests/eig_op_test.py b/tensorflow/python/kernel_tests/eig_op_test.py new file mode 100644 index 00000000000..a1d7ba9480e --- /dev/null +++ b/tensorflow/python/kernel_tests/eig_op_test.py @@ -0,0 +1,190 @@ +# Copyright 2016 The TensorFlow Authors. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ============================================================================== +"""Tests for tensorflow.ops.linalg_ops.eig.""" + +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +import numpy as np + +from tensorflow.python.framework import constant_op +from tensorflow.python.framework import dtypes as dtypes_lib +from tensorflow.python.framework import test_util +from tensorflow.python.ops import array_ops +from tensorflow.python.ops import gradient_checker_v2 +from tensorflow.python.ops import linalg_ops +from tensorflow.python.ops import math_ops +from tensorflow.python.ops import random_ops +from tensorflow.python.platform import test + + +def _AddTest(test_class, op_name, testcase_name, fn): + test_name = "_".join(["test", op_name, testcase_name]) + if hasattr(test_class, test_name): + raise RuntimeError("Test %s defined more than once" % test_name) + setattr(test_class, test_name, fn) + + +class EigTest(test.TestCase): + + @test_util.run_deprecated_v1 + def testWrongDimensions(self): + # The input to self_adjoint_eig should be a tensor of + # at least rank 2. + scalar = constant_op.constant(1.) + with self.assertRaises(ValueError): + linalg_ops.eig(scalar) + vector = constant_op.constant([1., 2.]) + with self.assertRaises(ValueError): + linalg_ops.eig(vector) + + @test_util.run_deprecated_v1 + def testConcurrentExecutesWithoutError(self): + all_ops = [] + with self.session(use_gpu=True) as sess: + for compute_v_ in True, False: + matrix1 = random_ops.random_normal([5, 5], seed=42) + matrix2 = random_ops.random_normal([5, 5], seed=42) + if compute_v_: + e1, v1 = linalg_ops.eig(matrix1) + e2, v2 = linalg_ops.eig(matrix2) + all_ops += [e1, v1, e2, v2] + else: + e1 = linalg_ops.eigvals(matrix1) + e2 = linalg_ops.eigvals(matrix2) + all_ops += [e1, e2] + val = self.evaluate(all_ops) + self.assertAllEqual(val[0], val[2]) + # The algorithm is slightly different for compute_v being True and False, + # so require approximate equality only here. + self.assertAllClose(val[2], val[4]) + self.assertAllEqual(val[4], val[5]) + self.assertAllEqual(val[1], val[3]) + + def testMatrixThatFailsWhenFlushingDenormsToZero(self): + # Test a 32x32 matrix which is known to fail if denorm floats are flushed to + # zero. + matrix = np.genfromtxt( + test.test_src_dir_path( + "python/kernel_tests/testdata/" + "self_adjoint_eig_fail_if_denorms_flushed.txt")).astype(np.float32) + self.assertEqual(matrix.shape, (32, 32)) + matrix_tensor = constant_op.constant(matrix) + with self.session(use_gpu=True) as sess: + (e, v) = self.evaluate(linalg_ops.self_adjoint_eig(matrix_tensor)) + self.assertEqual(e.size, 32) + self.assertAllClose( + np.matmul(v, v.transpose()), np.eye(32, dtype=np.float32), atol=2e-3) + self.assertAllClose(matrix, + np.matmul(np.matmul(v, np.diag(e)), v.transpose())) + + +def SortEigenDecomposition(e, v): + if v.ndim < 2: + return e, v + else: + perm = np.argsort(e, -1) + return np.take(e, perm, -1), np.take(v, perm, -1) + + +def EquilibrateEigenVectorPhases(x, y): + """Equilibrate the phase of the Eigenvectors in the columns of `x` and `y`. + + Eigenvectors are only unique up to an arbitrary phase. This function rotates x + such that it matches y. Precondition: The coluns of x and y differ by a + multiplicative complex phase factor only. + + Args: + x: `np.ndarray` with Eigenvectors + y: `np.ndarray` with Eigenvectors + + Returns: + `np.ndarray` containing an equilibrated version of x. + """ + phases = np.sum(np.conj(x) * y, -2, keepdims=True) + phases /= np.abs(phases) + return phases * x + + +def _GetEigTest(dtype_, shape_, compute_v_): + + def CompareEigenVectors(self, x, y, tol): + x = EquilibrateEigenVectorPhases(x, y) + self.assertAllClose(x, y, atol=tol) + + def CompareEigenDecompositions(self, x_e, x_v, y_e, y_v, tol): + num_batches = int(np.prod(x_e.shape[:-1])) + n = x_e.shape[-1] + x_e = np.reshape(x_e, [num_batches] + [n]) + x_v = np.reshape(x_v, [num_batches] + [n, n]) + y_e = np.reshape(y_e, [num_batches] + [n]) + y_v = np.reshape(y_v, [num_batches] + [n, n]) + for i in range(num_batches): + x_ei, x_vi = SortEigenDecomposition(x_e[i, :], x_v[i, :, :]) + y_ei, y_vi = SortEigenDecomposition(y_e[i, :], y_v[i, :, :]) + self.assertAllClose(x_ei, y_ei, atol=tol, rtol=tol) + CompareEigenVectors(self, x_vi, y_vi, tol) + + def Test(self): + np.random.seed(1) + n = shape_[-1] + batch_shape = shape_[:-2] + np_dtype = dtype_.as_numpy_dtype + # most of matrices are diagonalizable # TODO + a = np.random.uniform( + low=-1.0, high=1.0, size=n * n).reshape([n, n]).astype(np_dtype) + if dtype_.is_complex: + a += 1j * np.random.uniform( + low=-1.0, high=1.0, size=n * n).reshape([n, n]).astype(np_dtype) + a = np.tile(a, batch_shape + (1, 1)) + if dtype_ in (dtypes_lib.float32, dtypes_lib.complex64): + atol = 1e-4 + else: + atol = 1e-12 + np_e, np_v = np.linalg.eig(a) + with self.session(use_gpu=True): + if compute_v_: + tf_e, tf_v = linalg_ops.eig(constant_op.constant(a)) + + # Check that V*diag(E)*V^T is close to A. + a_ev = math_ops.matmul( + math_ops.matmul(tf_v, array_ops.matrix_diag(tf_e)), + tf_v, + adjoint_b=True) + self.assertAllClose(self.evaluate(a_ev), a, atol=atol) + + # Compare to numpy.linalg.eig. + CompareEigenDecompositions(self, np_e, np_v, self.evaluate(tf_e), + self.evaluate(tf_v), atol) + else: + tf_e = linalg_ops.eigvals(constant_op.constant(a)) + self.assertAllClose( + np.sort(np_e, -1), np.sort(self.evaluate(tf_e), -1), atol=atol) + + return Test + + +if __name__ == "__main__": + for compute_v in True, False: + for dtype in (dtypes_lib.complex64, dtypes_lib.complex128): + for size in 1, 2, 5, 10: + for batch_dims in [(), (3,)] + [(3, 2)] * (max(size, size) < 10): + shape = batch_dims + (size, size) + name = "%s_%s_%s" % (dtype.name, "_".join(map(str, shape)), compute_v) + _AddTest(EigTest, "Eig", name, + _GetEigTest(dtype, shape, compute_v)) + # No gradient yet + test.main() diff --git a/tensorflow/python/kernel_tests/self_adjoint_eig_op_test.py b/tensorflow/python/kernel_tests/self_adjoint_eig_op_test.py index a42d7922bfb..0ada446e84b 100644 --- a/tensorflow/python/kernel_tests/self_adjoint_eig_op_test.py +++ b/tensorflow/python/kernel_tests/self_adjoint_eig_op_test.py @@ -12,7 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. # ============================================================================== -"""Tests for tensorflow.ops.math_ops.matrix_inverse.""" +"""Tests for tensorflow.ops.linalg_ops.self_adjoint_eig.""" from __future__ import absolute_import from __future__ import division diff --git a/tensorflow/python/ops/linalg_ops.py b/tensorflow/python/ops/linalg_ops.py index 914e5748534..9362bace358 100644 --- a/tensorflow/python/ops/linalg_ops.py +++ b/tensorflow/python/ops/linalg_ops.py @@ -305,6 +305,49 @@ def matrix_solve_ls(matrix, rhs, l2_regularizer=0.0, fast=True, name=None): return gen_linalg_ops.matrix_solve_ls( matrix, rhs, l2_regularizer, fast=fast, name=name) +@tf_export('linalg.eig', v1=[]) +def eig(tensor, name=None): + """Computes the eigen decomposition of a batch of matrices. + + Computes the eigenvalues and eigenvectors of the innermost N-by-N matrices + in `tensor` such that + `tensor[...,:,:] * v[..., :,i] = e[..., i] * v[...,:,i]`, for i=0...N-1. + + Args: + tensor: `Tensor` of shape `[..., N, N]`. Only the lower triangular part of + each inner inner matrix is referenced. + name: string, optional name of the operation. + + Returns: + e: Eigenvalues. Shape is `[..., N]`. Sorted in non-decreasing order. + v: Eigenvectors. Shape is `[..., N, N]`. The columns of the inner most + matrices contain eigenvectors of the corresponding matrices in `tensor` + """ + e, v = gen_linalg_ops.eig(tensor, compute_v=True, name=name) + return e, v + + +@tf_export('linalg.eigvals', v1=[]) +@deprecation.deprecated_endpoints('self_adjoint_eigvals') +def eigvals(tensor, name=None): + """Computes the eigenvalues of one or more matrices. + + Note: If your program backpropagates through this function, you should replace + it with a call to tf.linalg.eig (possibly ignoring the second output) to + avoid computing the eigen decomposition twice. This is because the + eigenvectors are used to compute the gradient w.r.t. the eigenvalues. See + _SelfAdjointEigV2Grad in linalg_grad.py. + + Args: + tensor: `Tensor` of shape `[..., N, N]`. + name: string, optional name of the operation. + + Returns: + e: Eigenvalues. Shape is `[..., N]`. The vector `e[..., :]` contains the `N` + eigenvalues of `tensor[..., :, :]`. + """ + e, _ = gen_linalg_ops.eig(tensor, compute_v=False, name=name) + return e @tf_export('linalg.eigh', v1=['linalg.eigh', 'self_adjoint_eig']) @deprecation.deprecated_endpoints('self_adjoint_eig') diff --git a/tensorflow/tools/api/golden/v1/tensorflow.raw_ops.pbtxt b/tensorflow/tools/api/golden/v1/tensorflow.raw_ops.pbtxt index e7d5f1aec78..71c7c4db07e 100644 --- a/tensorflow/tools/api/golden/v1/tensorflow.raw_ops.pbtxt +++ b/tensorflow/tools/api/golden/v1/tensorflow.raw_ops.pbtxt @@ -3656,6 +3656,10 @@ tf_module { name: "SelfAdjointEig" argspec: "args=[\'input\', \'name\'], varargs=None, keywords=None, defaults=[\'None\'], " } + member_method { + name: "Eig" + argspec: "args=[\'input\', \'compute_v\', \'name\'], varargs=None, keywords=None, defaults=[\'True\', \'None\'], " + } member_method { name: "SelfAdjointEigV2" argspec: "args=[\'input\', \'compute_v\', \'name\'], varargs=None, keywords=None, defaults=[\'True\', \'None\'], " diff --git a/tensorflow/tools/api/golden/v2/tensorflow.raw_ops.pbtxt b/tensorflow/tools/api/golden/v2/tensorflow.raw_ops.pbtxt index e7d5f1aec78..71c7c4db07e 100644 --- a/tensorflow/tools/api/golden/v2/tensorflow.raw_ops.pbtxt +++ b/tensorflow/tools/api/golden/v2/tensorflow.raw_ops.pbtxt @@ -3656,6 +3656,10 @@ tf_module { name: "SelfAdjointEig" argspec: "args=[\'input\', \'name\'], varargs=None, keywords=None, defaults=[\'None\'], " } + member_method { + name: "Eig" + argspec: "args=[\'input\', \'compute_v\', \'name\'], varargs=None, keywords=None, defaults=[\'True\', \'None\'], " + } member_method { name: "SelfAdjointEigV2" argspec: "args=[\'input\', \'compute_v\', \'name\'], varargs=None, keywords=None, defaults=[\'True\', \'None\'], " From 7f7125e402d803ab3b291ff75bd3eab32ed0d731 Mon Sep 17 00:00:00 2001 From: Evgenii Zheltonozhskii Date: Fri, 3 May 2019 18:18:41 +0300 Subject: [PATCH 02/11] Remove non-existing test file from build --- tensorflow/compiler/tests/BUILD | 18 ------------------ tensorflow/core/kernels/eig_op_complex128.cc | 2 +- tensorflow/core/kernels/eig_op_impl.h | 2 +- tensorflow/python/kernel_tests/eig_op_test.py | 2 +- 4 files changed, 3 insertions(+), 21 deletions(-) diff --git a/tensorflow/compiler/tests/BUILD b/tensorflow/compiler/tests/BUILD index 511eeae6946..e4a5752efe0 100644 --- a/tensorflow/compiler/tests/BUILD +++ b/tensorflow/compiler/tests/BUILD @@ -256,24 +256,6 @@ tf_xla_py_test( ], ) -tf_xla_py_test( - name = "eig_op_test", - size = "medium", - srcs = ["eig_op_test.py"], - tags = ["optonly"], - deps = [ - ":xla_test", - "//tensorflow/python:array_ops", - "//tensorflow/python:framework", - "//tensorflow/python:map_fn", - "//tensorflow/python:math_ops", - "//tensorflow/python:platform_test", - "//tensorflow/python:training", - "@absl_py//absl/testing:parameterized", - ], -) - - tf_xla_py_test( name = "self_adjoint_eig_op_test", size = "medium", diff --git a/tensorflow/core/kernels/eig_op_complex128.cc b/tensorflow/core/kernels/eig_op_complex128.cc index 954b22f3def..d7cb7f32879 100644 --- a/tensorflow/core/kernels/eig_op_complex128.cc +++ b/tensorflow/core/kernels/eig_op_complex128.cc @@ -1,4 +1,4 @@ -/* Copyright 2017 The TensorFlow Authors. All Rights Reserved. +/* Copyright 2019 The TensorFlow Authors. All Rights Reserved. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/tensorflow/core/kernels/eig_op_impl.h b/tensorflow/core/kernels/eig_op_impl.h index 82ce7bdf5bd..c39eef1dcbc 100644 --- a/tensorflow/core/kernels/eig_op_impl.h +++ b/tensorflow/core/kernels/eig_op_impl.h @@ -1,4 +1,4 @@ -/* Copyright 2017 The TensorFlow Authors. All Rights Reserved. +/* Copyright 2019 The TensorFlow Authors. All Rights Reserved. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/tensorflow/python/kernel_tests/eig_op_test.py b/tensorflow/python/kernel_tests/eig_op_test.py index a1d7ba9480e..ea71c79ab57 100644 --- a/tensorflow/python/kernel_tests/eig_op_test.py +++ b/tensorflow/python/kernel_tests/eig_op_test.py @@ -1,4 +1,4 @@ -# Copyright 2016 The TensorFlow Authors. All Rights Reserved. +# Copyright 2019 The TensorFlow Authors. All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. From 6c0e3350d7bb0a09b412d45fa9ffd71d9969cc61 Mon Sep 17 00:00:00 2001 From: Evgenii Zheltonozhskii Date: Thu, 10 Oct 2019 14:17:32 +0300 Subject: [PATCH 03/11] Decompose ComplexEigOp for input and output type --- tensorflow/core/kernels/eig_op_complex128.cc | 2 +- tensorflow/core/kernels/eig_op_complex64.cc | 2 +- tensorflow/core/kernels/eig_op_impl.h | 41 ++++++++++++-------- tensorflow/python/ops/linalg_ops.py | 6 ++- 4 files changed, 30 insertions(+), 21 deletions(-) diff --git a/tensorflow/core/kernels/eig_op_complex128.cc b/tensorflow/core/kernels/eig_op_complex128.cc index d7cb7f32879..cbc28d82458 100644 --- a/tensorflow/core/kernels/eig_op_complex128.cc +++ b/tensorflow/core/kernels/eig_op_complex128.cc @@ -17,7 +17,7 @@ limitations under the License. namespace tensorflow { -REGISTER_LINALG_OP("Eig", (ComplexEigOp), +REGISTER_LINALG_OP("Eig", (ComplexEigOp), complex128); } // namespace tensorflow diff --git a/tensorflow/core/kernels/eig_op_complex64.cc b/tensorflow/core/kernels/eig_op_complex64.cc index 6af00394687..b15720b7174 100644 --- a/tensorflow/core/kernels/eig_op_complex64.cc +++ b/tensorflow/core/kernels/eig_op_complex64.cc @@ -17,7 +17,7 @@ limitations under the License. namespace tensorflow { -REGISTER_LINALG_OP("Eig", (ComplexEigOp), +REGISTER_LINALG_OP("Eig", (ComplexEigOp), complex64); } // namespace tensorflow diff --git a/tensorflow/core/kernels/eig_op_impl.h b/tensorflow/core/kernels/eig_op_impl.h index c39eef1dcbc..63683ddf73e 100644 --- a/tensorflow/core/kernels/eig_op_impl.h +++ b/tensorflow/core/kernels/eig_op_impl.h @@ -31,33 +31,40 @@ limitations under the License. namespace tensorflow { -template -class ComplexEigOp : public LinearAlgebraOp { +template +class ComplexEigOp : public LinearAlgebraOp { public: - typedef LinearAlgebraOp Base; + typedef LinearAlgebraOp InputBase; + typedef LinearAlgebraOp OutputBase; - explicit ComplexEigOp(OpKernelConstruction* context) : Base(context) { + explicit ComplexEigOp(OpKernelConstruction* context) : OutputBase(context) { OP_REQUIRES_OK(context, context->GetAttr("compute_v", &compute_v_)); } - using TensorShapes = typename Base::TensorShapes; - using Matrix = typename Base::Matrix; - using MatrixMaps = typename Base::MatrixMaps; - using ConstMatrixMap = typename Base::ConstMatrixMap; - using ConstMatrixMaps = typename Base::ConstMatrixMaps; + using InputTensorShapes = typename InputBase::TensorShapes; + using InputMatrix = typename InputBase::Matrix; + using InputMatrixMaps = typename InputBase::MatrixMaps; + using InputConstMatrixMap = typename InputBase::ConstMatrixMap; + using InputConstMatrixMaps = typename InputBase::ConstMatrixMaps; - TensorShapes GetOutputMatrixShapes( - const TensorShapes& input_matrix_shapes) const final { + using OutputTensorShapes = typename OutputBase::TensorShapes; + using OutputMatrix = typename OutputBase::Matrix; + using OutputMatrixMaps = typename OutputBase::MatrixMaps; + using OutputConstMatrixMap = typename OutputBase::ConstMatrixMap; + using OutputConstMatrixMaps = typename OutputBase::ConstMatrixMaps; + + OutputTensorShapes GetOutputMatrixShapes( + const InputTensorShapes& input_matrix_shapes) const final { int64 n = input_matrix_shapes[0].dim_size(0); if (compute_v_) { - return TensorShapes({TensorShape({n}), TensorShape({n, n})}); + return OutputTensorShapes({TensorShape({n}), TensorShape({n, n})}); } else { - return TensorShapes({TensorShape({n})}); + return OutputTensorShapes({TensorShape({n})}); } } - void ComputeMatrix(OpKernelContext* context, const ConstMatrixMaps& inputs, - MatrixMaps* outputs) final { + void ComputeMatrix(OpKernelContext* context, const InputConstMatrixMaps& inputs, + OutputMatrixMaps* outputs) final { const int64 rows = inputs[0].rows(); if (rows == 0) { // If X is an empty matrix (0 rows, 0 col), X * X' == X. @@ -68,7 +75,7 @@ class ComplexEigOp : public LinearAlgebraOp { // This algorithm relies on denormals, so switch them back on locally. port::ScopedDontFlushDenormal dont_flush_denormals; - Eigen::ComplexEigenSolver eig( + Eigen::ComplexEigenSolver eig( inputs[0], compute_v_ ? Eigen::ComputeEigenvectors : Eigen::EigenvaluesOnly); // TODO(rmlarsen): Output more detailed error info on failure. @@ -77,7 +84,7 @@ class ComplexEigOp : public LinearAlgebraOp { errors::InvalidArgument("Eigen decomposition was not " "successful. The input might not be valid.")); - outputs->at(0) = eig.eigenvalues().template cast(); + outputs->at(0) = eig.eigenvalues().template cast(); if (compute_v_) { outputs->at(1) = eig.eigenvectors(); } diff --git a/tensorflow/python/ops/linalg_ops.py b/tensorflow/python/ops/linalg_ops.py index 9362bace358..10415f2ab79 100644 --- a/tensorflow/python/ops/linalg_ops.py +++ b/tensorflow/python/ops/linalg_ops.py @@ -307,9 +307,11 @@ def matrix_solve_ls(matrix, rhs, l2_regularizer=0.0, fast=True, name=None): @tf_export('linalg.eig', v1=[]) def eig(tensor, name=None): - """Computes the eigen decomposition of a batch of matrices. + """Computes the eigen decomposition of a batch of matrices. The eigenvalues + and eigenvectors for a non-Hermitian matrix in general are complex. The + eigenvectors are not guaranteed to be linearly independent. - Computes the eigenvalues and eigenvectors of the innermost N-by-N matrices + Computes the eigenvalues and right eigenvectors of the innermost N-by-N matrices in `tensor` such that `tensor[...,:,:] * v[..., :,i] = e[..., i] * v[...,:,i]`, for i=0...N-1. From f43dea7ca9aae6aa6b2fe5dffc6fd02c64c1afd8 Mon Sep 17 00:00:00 2001 From: Evgenii Zheltonozhskii Date: Fri, 11 Oct 2019 02:01:01 +0300 Subject: [PATCH 04/11] Decompose LinearAlgebraOp for different input and output types, apply to eig --- .../core/api_def/python_api/api_def_Eig.pbtxt | 4 ++ tensorflow/core/kernels/BUILD | 2 +- tensorflow/core/kernels/eig_op_complex128.cc | 2 +- tensorflow/core/kernels/eig_op_complex64.cc | 2 +- tensorflow/core/kernels/eig_op_double.cc | 23 ++++++++++ tensorflow/core/kernels/eig_op_float.cc | 23 ++++++++++ tensorflow/core/kernels/eig_op_impl.h | 34 +++++++-------- tensorflow/core/kernels/linalg_ops_common.cc | 42 ++++++++++--------- tensorflow/core/kernels/linalg_ops_common.h | 29 +++++++++++-- tensorflow/core/ops/linalg_ops.cc | 7 ++-- tensorflow/python/kernel_tests/eig_op_test.py | 7 +++- tensorflow/python/ops/linalg_ops.py | 22 ++++++---- 12 files changed, 141 insertions(+), 56 deletions(-) create mode 100644 tensorflow/core/api_def/python_api/api_def_Eig.pbtxt create mode 100644 tensorflow/core/kernels/eig_op_double.cc create mode 100644 tensorflow/core/kernels/eig_op_float.cc diff --git a/tensorflow/core/api_def/python_api/api_def_Eig.pbtxt b/tensorflow/core/api_def/python_api/api_def_Eig.pbtxt new file mode 100644 index 00000000000..08a413a9941 --- /dev/null +++ b/tensorflow/core/api_def/python_api/api_def_Eig.pbtxt @@ -0,0 +1,4 @@ +op { + graph_op_name: "Eig" + visibility: HIDDEN +} diff --git a/tensorflow/core/kernels/BUILD b/tensorflow/core/kernels/BUILD index 8716a97cbbe..e0625f3a330 100644 --- a/tensorflow/core/kernels/BUILD +++ b/tensorflow/core/kernels/BUILD @@ -3361,6 +3361,7 @@ cc_library( ":cholesky_grad", ":cholesky_op", ":determinant_op", + ":eig_op", ":einsum_op", ":lu_op", ":matrix_exponential_op", @@ -3373,7 +3374,6 @@ cc_library( ":qr_op", ":self_adjoint_eig_op", ":self_adjoint_eig_v2_op", - ":eig_op", ":svd_op", ":tridiagonal_matmul_op", ":tridiagonal_solve_op", diff --git a/tensorflow/core/kernels/eig_op_complex128.cc b/tensorflow/core/kernels/eig_op_complex128.cc index cbc28d82458..488dfc8203d 100644 --- a/tensorflow/core/kernels/eig_op_complex128.cc +++ b/tensorflow/core/kernels/eig_op_complex128.cc @@ -17,7 +17,7 @@ limitations under the License. namespace tensorflow { -REGISTER_LINALG_OP("Eig", (ComplexEigOp), +REGISTER_LINALG_OP("Eig", (EigOp), complex128); } // namespace tensorflow diff --git a/tensorflow/core/kernels/eig_op_complex64.cc b/tensorflow/core/kernels/eig_op_complex64.cc index b15720b7174..0ea0e573c78 100644 --- a/tensorflow/core/kernels/eig_op_complex64.cc +++ b/tensorflow/core/kernels/eig_op_complex64.cc @@ -17,7 +17,7 @@ limitations under the License. namespace tensorflow { -REGISTER_LINALG_OP("Eig", (ComplexEigOp), +REGISTER_LINALG_OP("Eig", (EigOp), complex64); } // namespace tensorflow diff --git a/tensorflow/core/kernels/eig_op_double.cc b/tensorflow/core/kernels/eig_op_double.cc new file mode 100644 index 00000000000..ef0d69e9ccc --- /dev/null +++ b/tensorflow/core/kernels/eig_op_double.cc @@ -0,0 +1,23 @@ +/* Copyright 2019 The TensorFlow Authors. All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +==============================================================================*/ + +#include "tensorflow/core/kernels/eig_op_impl.h" + +namespace tensorflow { + +REGISTER_LINALG_OP("Eig", (EigOp), + double); + +} // namespace tensorflow diff --git a/tensorflow/core/kernels/eig_op_float.cc b/tensorflow/core/kernels/eig_op_float.cc new file mode 100644 index 00000000000..d32b6f55f1c --- /dev/null +++ b/tensorflow/core/kernels/eig_op_float.cc @@ -0,0 +1,23 @@ +/* Copyright 2019 The TensorFlow Authors. All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +==============================================================================*/ + +#include "tensorflow/core/kernels/eig_op_impl.h" + +namespace tensorflow { + +REGISTER_LINALG_OP("Eig", (EigOp), + float); + +} // namespace tensorflow diff --git a/tensorflow/core/kernels/eig_op_impl.h b/tensorflow/core/kernels/eig_op_impl.h index 63683ddf73e..38fc526f7e7 100644 --- a/tensorflow/core/kernels/eig_op_impl.h +++ b/tensorflow/core/kernels/eig_op_impl.h @@ -32,34 +32,32 @@ limitations under the License. namespace tensorflow { template -class ComplexEigOp : public LinearAlgebraOp { +class EigOp : public LinearAlgebraOp { public: - typedef LinearAlgebraOp InputBase; - typedef LinearAlgebraOp OutputBase; + typedef LinearAlgebraOp Base; - explicit ComplexEigOp(OpKernelConstruction* context) : OutputBase(context) { + explicit EigOp(OpKernelConstruction* context) : Base(context) { OP_REQUIRES_OK(context, context->GetAttr("compute_v", &compute_v_)); } - using InputTensorShapes = typename InputBase::TensorShapes; - using InputMatrix = typename InputBase::Matrix; - using InputMatrixMaps = typename InputBase::MatrixMaps; - using InputConstMatrixMap = typename InputBase::ConstMatrixMap; - using InputConstMatrixMaps = typename InputBase::ConstMatrixMaps; + using TensorShapes = typename Base::TensorShapes; + using InputMatrix = typename Base::InputMatrix; + using InputMatrixMaps = typename Base::InputMatrixMaps; + using InputConstMatrixMap = typename Base::InputConstMatrixMap; + using InputConstMatrixMaps = typename Base::InputConstMatrixMaps; - using OutputTensorShapes = typename OutputBase::TensorShapes; - using OutputMatrix = typename OutputBase::Matrix; - using OutputMatrixMaps = typename OutputBase::MatrixMaps; - using OutputConstMatrixMap = typename OutputBase::ConstMatrixMap; - using OutputConstMatrixMaps = typename OutputBase::ConstMatrixMaps; + using OutputMatrix = typename Base::OutputMatrix; + using OutputMatrixMaps = typename Base::OutputMatrixMaps; + using OutputConstMatrixMap = typename Base::OutputConstMatrixMap; + using OutputConstMatrixMaps = typename Base::OutputConstMatrixMaps; - OutputTensorShapes GetOutputMatrixShapes( - const InputTensorShapes& input_matrix_shapes) const final { + TensorShapes GetOutputMatrixShapes( + const TensorShapes& input_matrix_shapes) const final { int64 n = input_matrix_shapes[0].dim_size(0); if (compute_v_) { - return OutputTensorShapes({TensorShape({n}), TensorShape({n, n})}); + return TensorShapes({TensorShape({n}), TensorShape({n, n})}); } else { - return OutputTensorShapes({TensorShape({n})}); + return TensorShapes({TensorShape({n})}); } } diff --git a/tensorflow/core/kernels/linalg_ops_common.cc b/tensorflow/core/kernels/linalg_ops_common.cc index b58bcf58348..24297a42206 100644 --- a/tensorflow/core/kernels/linalg_ops_common.cc +++ b/tensorflow/core/kernels/linalg_ops_common.cc @@ -29,8 +29,8 @@ limitations under the License. namespace tensorflow { // static -template -void LinearAlgebraOp::ValidateSingleMatrix( +template +void LinearAlgebraOp::ValidateSingleMatrix( OpKernelContext* context, const TensorShapes& input_matrix_shapes) { OP_REQUIRES(context, input_matrix_shapes.size() == 1, errors::InvalidArgument("Expected a single input matrix, got %d.", @@ -40,8 +40,8 @@ void LinearAlgebraOp::ValidateSingleMatrix( } // static -template -void LinearAlgebraOp::ValidateSingleSquareMatrix( +template +void LinearAlgebraOp::ValidateSingleSquareMatrix( OpKernelContext* context, const TensorShapes& input_matrix_shapes) { OP_REQUIRES(context, input_matrix_shapes.size() == 1, errors::InvalidArgument("Expected a single input matrix, got %d.", @@ -51,8 +51,8 @@ void LinearAlgebraOp::ValidateSingleSquareMatrix( } // static -template -void LinearAlgebraOp::ValidateSolver( +template +void LinearAlgebraOp::ValidateSolver( OpKernelContext* context, const TensorShapes& input_matrix_shapes) { OP_REQUIRES(context, input_matrix_shapes.size() == 2, errors::InvalidArgument("Expected two input matrices, got %d.", @@ -68,8 +68,8 @@ void LinearAlgebraOp::ValidateSolver( } // static -template -void LinearAlgebraOp::ValidateSquareSolver( +template +void LinearAlgebraOp::ValidateSquareSolver( OpKernelContext* context, const TensorShapes& input_matrix_shapes) { OP_REQUIRES(context, input_matrix_shapes.size() == 2, errors::InvalidArgument("Expected two input matrices, got %d.", @@ -85,8 +85,8 @@ void LinearAlgebraOp::ValidateSquareSolver( errors::InvalidArgument("Input matrix and rhs are incompatible.")); } -template -void LinearAlgebraOp::Compute(OpKernelContext* context) { +template +void LinearAlgebraOp::Compute(OpKernelContext* context) { TensorInputs inputs; TensorShapes input_matrix_shapes; TensorShape batch_shape; @@ -110,8 +110,8 @@ void LinearAlgebraOp::Compute(OpKernelContext* context) { batch_shape.num_elements(), GetCostPerUnit(input_matrix_shapes), shard); } -template -void LinearAlgebraOp::AnalyzeInputs(OpKernelContext* context, +template +void LinearAlgebraOp::AnalyzeInputs(OpKernelContext* context, TensorInputs* inputs, TensorShapes* input_matrix_shapes, TensorShape* batch_shape) { @@ -155,8 +155,8 @@ void LinearAlgebraOp::AnalyzeInputs(OpKernelContext* context, ValidateInputMatrixShapes(context, *input_matrix_shapes); } -template -void LinearAlgebraOp::PrepareOutputs( +template +void LinearAlgebraOp::PrepareOutputs( OpKernelContext* context, const TensorShapes& input_matrix_shapes, const TensorShape& batch_shape, TensorOutputs* outputs, TensorShapes* output_matrix_shapes) { @@ -214,22 +214,22 @@ void LinearAlgebraOp::PrepareOutputs( } } -template -void LinearAlgebraOp::ComputeTensorSlice( +template +void LinearAlgebraOp::ComputeTensorSlice( OpKernelContext* context, int64 matrix_index, const TensorInputs& inputs, const TensorShapes& input_matrix_shapes, const TensorOutputs& outputs, const TensorShapes& output_matrix_shapes) { - ConstMatrixMaps matrix_inputs; + InputConstMatrixMaps matrix_inputs; for (size_t i = 0; i < inputs.size(); ++i) { // TODO(kalakris): Handle alignment if possible. Eigen::Map is // unaligned by default. matrix_inputs.emplace_back( - inputs[i]->flat().data() + + inputs[i]->flat().data() + matrix_index * input_matrix_shapes[i].num_elements(), input_matrix_shapes[i].dim_size(0), input_matrix_shapes[i].dim_size(1)); } - MatrixMaps matrix_outputs; + OutputMatrixMaps matrix_outputs; for (size_t i = 0; i < output_matrix_shapes.size(); ++i) { // The output matrix shape may not be a matrix. int num_output_rows = output_matrix_shapes[i].dims() >= 1 @@ -239,7 +239,7 @@ void LinearAlgebraOp::ComputeTensorSlice( ? output_matrix_shapes[i].dim_size(1) : 1; matrix_outputs.emplace_back( - outputs[i]->flat().data() + + outputs[i]->flat().data() + matrix_index * output_matrix_shapes[i].num_elements(), num_output_rows, num_output_cols); } @@ -251,5 +251,7 @@ template class LinearAlgebraOp; template class LinearAlgebraOp; template class LinearAlgebraOp; template class LinearAlgebraOp; +template class LinearAlgebraOp; +template class LinearAlgebraOp; } // namespace tensorflow diff --git a/tensorflow/core/kernels/linalg_ops_common.h b/tensorflow/core/kernels/linalg_ops_common.h index 11ecf7d676e..c154a424397 100644 --- a/tensorflow/core/kernels/linalg_ops_common.h +++ b/tensorflow/core/kernels/linalg_ops_common.h @@ -36,7 +36,7 @@ limitations under the License. namespace tensorflow { // Base class for linear algebra operators. -template +template class LinearAlgebraOp : public OpKernel { public: explicit LinearAlgebraOp(OpKernelConstruction* context) : OpKernel(context) {} @@ -109,6 +109,29 @@ class LinearAlgebraOp : public OpKernel { // and expect the kernel to perform the computation inplace. virtual bool EnableInputForwarding() const { return true; } + using InputMatrix = + Eigen::Matrix; + using InputConstMatrixMap = Eigen::Map; + using InputMatrixMap = Eigen::Map; + using InputConstVectorMap = + Eigen::Map>; + using InputConstMatrixMaps = gtl::InlinedVector; + using InputMatrixMaps = gtl::InlinedVector; + using InputRealScalar = typename Eigen::NumTraits::Real; + + + using OutputMatrix = + Eigen::Matrix; + using OutputConstMatrixMap = Eigen::Map; + using OutputMatrixMap = Eigen::Map; + using OutputConstVectorMap = + Eigen::Map>; + using OutputConstMatrixMaps = gtl::InlinedVector; + using OutputMatrixMaps = gtl::InlinedVector; + using OutputRealScalar = typename Eigen::NumTraits::Real; + + // backward compatibility + using Scalar =OutputScalar; using Matrix = Eigen::Matrix; using ConstMatrixMap = Eigen::Map; @@ -126,8 +149,8 @@ class LinearAlgebraOp : public OpKernel { // parallelized. The number of threads used is determined by a cost model from // the value returned by GetCostPerUnit(). virtual void ComputeMatrix(OpKernelContext* context, - const ConstMatrixMaps& inputs, - MatrixMaps* outputs) = 0; + const InputConstMatrixMaps& inputs, + OutputMatrixMaps* outputs) = 0; private: using TensorInputs = gtl::InlinedVector; diff --git a/tensorflow/core/ops/linalg_ops.cc b/tensorflow/core/ops/linalg_ops.cc index 7ee83b2ce44..4572df279b7 100644 --- a/tensorflow/core/ops/linalg_ops.cc +++ b/tensorflow/core/ops/linalg_ops.cc @@ -385,10 +385,11 @@ REGISTER_OP("SelfAdjointEig") REGISTER_OP("Eig") .Input("input: T") - .Output("e: T") - .Output("v: T") + .Output("e: Tout") + .Output("v: Tout") .Attr("compute_v: bool = True") - .Attr("T: {complex64, complex128}") + .Attr("T: {float, double, complex64, complex128}") + .Attr("Tout: {complex64, complex128}") .SetShapeFn(SelfAdjointEigV2ShapeFn); REGISTER_OP("SelfAdjointEigV2") diff --git a/tensorflow/python/kernel_tests/eig_op_test.py b/tensorflow/python/kernel_tests/eig_op_test.py index ea71c79ab57..40258281ebb 100644 --- a/tensorflow/python/kernel_tests/eig_op_test.py +++ b/tensorflow/python/kernel_tests/eig_op_test.py @@ -24,7 +24,6 @@ from tensorflow.python.framework import constant_op from tensorflow.python.framework import dtypes as dtypes_lib from tensorflow.python.framework import test_util from tensorflow.python.ops import array_ops -from tensorflow.python.ops import gradient_checker_v2 from tensorflow.python.ops import linalg_ops from tensorflow.python.ops import math_ops from tensorflow.python.ops import random_ops @@ -178,8 +177,12 @@ def _GetEigTest(dtype_, shape_, compute_v_): if __name__ == "__main__": + dtypes_to_test = [dtypes_lib.float32, dtypes_lib.float64] + if not test.is_built_with_rocm(): + # ROCm does not support BLAS operations for complex types + dtypes_to_test += [dtypes_lib.complex64, dtypes_lib.complex128] for compute_v in True, False: - for dtype in (dtypes_lib.complex64, dtypes_lib.complex128): + for dtype in dtypes_to_test: for size in 1, 2, 5, 10: for batch_dims in [(), (3,)] + [(3, 2)] * (max(size, size) < 10): shape = batch_dims + (size, size) diff --git a/tensorflow/python/ops/linalg_ops.py b/tensorflow/python/ops/linalg_ops.py index 10415f2ab79..701b35ae59f 100644 --- a/tensorflow/python/ops/linalg_ops.py +++ b/tensorflow/python/ops/linalg_ops.py @@ -305,14 +305,15 @@ def matrix_solve_ls(matrix, rhs, l2_regularizer=0.0, fast=True, name=None): return gen_linalg_ops.matrix_solve_ls( matrix, rhs, l2_regularizer, fast=fast, name=name) -@tf_export('linalg.eig', v1=[]) + +@tf_export('eig','linalg.eig', v1=[]) def eig(tensor, name=None): """Computes the eigen decomposition of a batch of matrices. The eigenvalues and eigenvectors for a non-Hermitian matrix in general are complex. The eigenvectors are not guaranteed to be linearly independent. - Computes the eigenvalues and right eigenvectors of the innermost N-by-N matrices - in `tensor` such that + Computes the eigenvalues and right eigenvectors of the innermost + N-by-N matrices in `tensor` such that `tensor[...,:,:] * v[..., :,i] = e[..., i] * v[...,:,i]`, for i=0...N-1. Args: @@ -325,12 +326,15 @@ def eig(tensor, name=None): v: Eigenvectors. Shape is `[..., N, N]`. The columns of the inner most matrices contain eigenvectors of the corresponding matrices in `tensor` """ - e, v = gen_linalg_ops.eig(tensor, compute_v=True, name=name) + if tensor.dtype == dtypes.float32 or tensor.dtype == dtypes.complex64: + out_dtype = dtypes.complex64 + elif tensor.dtype == dtypes.float64 or tensor.dtype == dtypes.complex128: + out_dtype = dtypes.complex128 + e, v = gen_linalg_ops.eig(tensor, Tout=out_dtype, compute_v=True, name=name) return e, v -@tf_export('linalg.eigvals', v1=[]) -@deprecation.deprecated_endpoints('self_adjoint_eigvals') +@tf_export('eigvals','linalg.eigvals', v1=[]) def eigvals(tensor, name=None): """Computes the eigenvalues of one or more matrices. @@ -348,7 +352,11 @@ def eigvals(tensor, name=None): e: Eigenvalues. Shape is `[..., N]`. The vector `e[..., :]` contains the `N` eigenvalues of `tensor[..., :, :]`. """ - e, _ = gen_linalg_ops.eig(tensor, compute_v=False, name=name) + if tensor.dtype == dtypes.float32 or tensor.dtype == dtypes.complex64: + out_dtype = dtypes.complex64 + elif tensor.dtype == dtypes.float64 or tensor.dtype == dtypes.complex128: + out_dtype = dtypes.complex128 + e, _ = gen_linalg_ops._eig(tensor, Tout=out_dtype, compute_v=False, name=name) return e @tf_export('linalg.eigh', v1=['linalg.eigh', 'self_adjoint_eig']) From 3602f66fff596cccc7c48beda9ced2dd7b33fa30 Mon Sep 17 00:00:00 2001 From: Evgenii Zheltonozhskii Date: Fri, 11 Oct 2019 02:04:18 +0300 Subject: [PATCH 05/11] Fix typedef --- tensorflow/core/kernels/eig_op_impl.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tensorflow/core/kernels/eig_op_impl.h b/tensorflow/core/kernels/eig_op_impl.h index 38fc526f7e7..dd07cc9a3fb 100644 --- a/tensorflow/core/kernels/eig_op_impl.h +++ b/tensorflow/core/kernels/eig_op_impl.h @@ -34,7 +34,7 @@ namespace tensorflow { template class EigOp : public LinearAlgebraOp { public: - typedef LinearAlgebraOp Base; + typedef LinearAlgebraOp Base; explicit EigOp(OpKernelConstruction* context) : Base(context) { OP_REQUIRES_OK(context, context->GetAttr("compute_v", &compute_v_)); From 0f0086a8e55a799fef4ef777260672edb86f89ba Mon Sep 17 00:00:00 2001 From: Evgenii Zheltonozhskii Date: Fri, 11 Oct 2019 02:17:00 +0300 Subject: [PATCH 06/11] Fix op --- tensorflow/core/kernels/eig_op_complex128.cc | 2 +- tensorflow/core/kernels/eig_op_complex64.cc | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tensorflow/core/kernels/eig_op_complex128.cc b/tensorflow/core/kernels/eig_op_complex128.cc index 488dfc8203d..a9b89297e0e 100644 --- a/tensorflow/core/kernels/eig_op_complex128.cc +++ b/tensorflow/core/kernels/eig_op_complex128.cc @@ -17,7 +17,7 @@ limitations under the License. namespace tensorflow { -REGISTER_LINALG_OP("Eig", (EigOp), +REGISTER_LINALG_OP("Eig", (EigOp), complex128); } // namespace tensorflow diff --git a/tensorflow/core/kernels/eig_op_complex64.cc b/tensorflow/core/kernels/eig_op_complex64.cc index 0ea0e573c78..3a1aed794dd 100644 --- a/tensorflow/core/kernels/eig_op_complex64.cc +++ b/tensorflow/core/kernels/eig_op_complex64.cc @@ -17,7 +17,7 @@ limitations under the License. namespace tensorflow { -REGISTER_LINALG_OP("Eig", (EigOp), +REGISTER_LINALG_OP("Eig", (EigOp), complex64); } // namespace tensorflow From e6a9247bbeaa52113bec454dc0e55285c416f78b Mon Sep 17 00:00:00 2001 From: Evgenii Zheltonozhskii Date: Fri, 11 Oct 2019 20:11:03 +0300 Subject: [PATCH 07/11] pylint --- tensorflow/python/ops/linalg_ops.py | 14 +++++++------- .../tools/api/golden/v1/tensorflow.linalg.pbtxt | 8 ++++++++ .../tools/api/golden/v1/tensorflow.raw_ops.pbtxt | 2 +- .../tools/api/golden/v2/tensorflow.linalg.pbtxt | 8 ++++++++ .../tools/api/golden/v2/tensorflow.raw_ops.pbtxt | 2 +- 5 files changed, 25 insertions(+), 9 deletions(-) diff --git a/tensorflow/python/ops/linalg_ops.py b/tensorflow/python/ops/linalg_ops.py index 701b35ae59f..6e3ac12f238 100644 --- a/tensorflow/python/ops/linalg_ops.py +++ b/tensorflow/python/ops/linalg_ops.py @@ -306,7 +306,7 @@ def matrix_solve_ls(matrix, rhs, l2_regularizer=0.0, fast=True, name=None): matrix, rhs, l2_regularizer, fast=fast, name=name) -@tf_export('eig','linalg.eig', v1=[]) +@tf_export('eig', 'linalg.eig', v1=[]) def eig(tensor, name=None): """Computes the eigen decomposition of a batch of matrices. The eigenvalues and eigenvectors for a non-Hermitian matrix in general are complex. The @@ -327,14 +327,14 @@ def eig(tensor, name=None): matrices contain eigenvectors of the corresponding matrices in `tensor` """ if tensor.dtype == dtypes.float32 or tensor.dtype == dtypes.complex64: - out_dtype = dtypes.complex64 + out_dtype = dtypes.complex64 elif tensor.dtype == dtypes.float64 or tensor.dtype == dtypes.complex128: - out_dtype = dtypes.complex128 + out_dtype = dtypes.complex128 e, v = gen_linalg_ops.eig(tensor, Tout=out_dtype, compute_v=True, name=name) return e, v -@tf_export('eigvals','linalg.eigvals', v1=[]) +@tf_export('eigvals', 'linalg.eigvals', v1=[]) def eigvals(tensor, name=None): """Computes the eigenvalues of one or more matrices. @@ -353,10 +353,10 @@ def eigvals(tensor, name=None): eigenvalues of `tensor[..., :, :]`. """ if tensor.dtype == dtypes.float32 or tensor.dtype == dtypes.complex64: - out_dtype = dtypes.complex64 + out_dtype = dtypes.complex64 elif tensor.dtype == dtypes.float64 or tensor.dtype == dtypes.complex128: - out_dtype = dtypes.complex128 - e, _ = gen_linalg_ops._eig(tensor, Tout=out_dtype, compute_v=False, name=name) + out_dtype = dtypes.complex128 + e, _ = gen_linalg_ops.eig(tensor, Tout=out_dtype, compute_v=False, name=name) return e @tf_export('linalg.eigh', v1=['linalg.eigh', 'self_adjoint_eig']) diff --git a/tensorflow/tools/api/golden/v1/tensorflow.linalg.pbtxt b/tensorflow/tools/api/golden/v1/tensorflow.linalg.pbtxt index 283fd9c35d6..8bd6672416b 100644 --- a/tensorflow/tools/api/golden/v1/tensorflow.linalg.pbtxt +++ b/tensorflow/tools/api/golden/v1/tensorflow.linalg.pbtxt @@ -108,10 +108,18 @@ tf_module { name: "diag_part" argspec: "args=[\'input\', \'name\', \'k\', \'padding_value\'], varargs=None, keywords=None, defaults=[\'diag_part\', \'0\', \'0\'], " } + member_method { + name: "eig" + argspec: "args=[\'tensor\', \'name\'], varargs=None, keywords=None, defaults=[\'None\'], " + } member_method { name: "eigh" argspec: "args=[\'tensor\', \'name\'], varargs=None, keywords=None, defaults=[\'None\'], " } + member_method { + name: "eigvals" + argspec: "args=[\'tensor\', \'name\'], varargs=None, keywords=None, defaults=[\'None\'], " + } member_method { name: "eigvalsh" argspec: "args=[\'tensor\', \'name\'], varargs=None, keywords=None, defaults=[\'None\'], " diff --git a/tensorflow/tools/api/golden/v1/tensorflow.raw_ops.pbtxt b/tensorflow/tools/api/golden/v1/tensorflow.raw_ops.pbtxt index 71c7c4db07e..79e4810c7f4 100644 --- a/tensorflow/tools/api/golden/v1/tensorflow.raw_ops.pbtxt +++ b/tensorflow/tools/api/golden/v1/tensorflow.raw_ops.pbtxt @@ -3658,7 +3658,7 @@ tf_module { } member_method { name: "Eig" - argspec: "args=[\'input\', \'compute_v\', \'name\'], varargs=None, keywords=None, defaults=[\'True\', \'None\'], " + argspec: "args=[\'input\', \'Tout\', \'compute_v\', \'name\'], varargs=None, keywords=None, defaults=[\'True\', \'None\'], " } member_method { name: "SelfAdjointEigV2" diff --git a/tensorflow/tools/api/golden/v2/tensorflow.linalg.pbtxt b/tensorflow/tools/api/golden/v2/tensorflow.linalg.pbtxt index 3150ea14464..a25583d7fdd 100644 --- a/tensorflow/tools/api/golden/v2/tensorflow.linalg.pbtxt +++ b/tensorflow/tools/api/golden/v2/tensorflow.linalg.pbtxt @@ -108,10 +108,18 @@ tf_module { name: "diag_part" argspec: "args=[\'input\', \'name\', \'k\', \'padding_value\'], varargs=None, keywords=None, defaults=[\'diag_part\', \'0\', \'0\'], " } + member_method { + name: "eig" + argspec: "args=[\'tensor\', \'name\'], varargs=None, keywords=None, defaults=[\'None\'], " + } member_method { name: "eigh" argspec: "args=[\'tensor\', \'name\'], varargs=None, keywords=None, defaults=[\'None\'], " } + member_method { + name: "eigvals" + argspec: "args=[\'tensor\', \'name\'], varargs=None, keywords=None, defaults=[\'None\'], " + } member_method { name: "eigvalsh" argspec: "args=[\'tensor\', \'name\'], varargs=None, keywords=None, defaults=[\'None\'], " diff --git a/tensorflow/tools/api/golden/v2/tensorflow.raw_ops.pbtxt b/tensorflow/tools/api/golden/v2/tensorflow.raw_ops.pbtxt index 71c7c4db07e..79e4810c7f4 100644 --- a/tensorflow/tools/api/golden/v2/tensorflow.raw_ops.pbtxt +++ b/tensorflow/tools/api/golden/v2/tensorflow.raw_ops.pbtxt @@ -3658,7 +3658,7 @@ tf_module { } member_method { name: "Eig" - argspec: "args=[\'input\', \'compute_v\', \'name\'], varargs=None, keywords=None, defaults=[\'True\', \'None\'], " + argspec: "args=[\'input\', \'Tout\', \'compute_v\', \'name\'], varargs=None, keywords=None, defaults=[\'True\', \'None\'], " } member_method { name: "SelfAdjointEigV2" From 9ba9fe64cfa96b2a43d8b890ab340dfdc352a5d0 Mon Sep 17 00:00:00 2001 From: Evgenii Zheltonozhskii Date: Fri, 11 Oct 2019 21:18:21 +0300 Subject: [PATCH 08/11] Fix eig tests --- tensorflow/python/kernel_tests/eig_op_test.py | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/tensorflow/python/kernel_tests/eig_op_test.py b/tensorflow/python/kernel_tests/eig_op_test.py index 40258281ebb..89734b3cd8a 100644 --- a/tensorflow/python/kernel_tests/eig_op_test.py +++ b/tensorflow/python/kernel_tests/eig_op_test.py @@ -91,11 +91,15 @@ class EigTest(test.TestCase): np.matmul(np.matmul(v, np.diag(e)), v.transpose())) +def SortEigenValues(e): + perm = np.argsort(e.real+e.imag, -1) + return np.take(e, perm, -1) + def SortEigenDecomposition(e, v): if v.ndim < 2: return e, v else: - perm = np.argsort(e, -1) + perm = np.argsort(e.real+e.imag, -1) return np.take(e, perm, -1), np.take(v, perm, -1) @@ -158,11 +162,10 @@ def _GetEigTest(dtype_, shape_, compute_v_): if compute_v_: tf_e, tf_v = linalg_ops.eig(constant_op.constant(a)) - # Check that V*diag(E)*V^T is close to A. + # Check that V*diag(E)*V^(-1) is close to A. a_ev = math_ops.matmul( math_ops.matmul(tf_v, array_ops.matrix_diag(tf_e)), - tf_v, - adjoint_b=True) + linalg_ops.matrix_inverse(tf_v)) self.assertAllClose(self.evaluate(a_ev), a, atol=atol) # Compare to numpy.linalg.eig. @@ -171,7 +174,8 @@ def _GetEigTest(dtype_, shape_, compute_v_): else: tf_e = linalg_ops.eigvals(constant_op.constant(a)) self.assertAllClose( - np.sort(np_e, -1), np.sort(self.evaluate(tf_e), -1), atol=atol) + SortEigenValues(np_e), SortEigenValues(self.evaluate(tf_e)), + atol=atol) return Test From 62558b1b6d2eee6991c84d58c35195f67771e643 Mon Sep 17 00:00:00 2001 From: Evgenii Zheltonozhskii Date: Fri, 11 Oct 2019 21:33:11 +0300 Subject: [PATCH 09/11] Golden update --- tensorflow/tools/api/golden/v1/tensorflow.linalg.pbtxt | 8 -------- tensorflow/tools/api/golden/v1/tensorflow.raw_ops.pbtxt | 8 ++++---- tensorflow/tools/api/golden/v2/tensorflow.pbtxt | 8 ++++++++ tensorflow/tools/api/golden/v2/tensorflow.raw_ops.pbtxt | 8 ++++---- 4 files changed, 16 insertions(+), 16 deletions(-) diff --git a/tensorflow/tools/api/golden/v1/tensorflow.linalg.pbtxt b/tensorflow/tools/api/golden/v1/tensorflow.linalg.pbtxt index 8bd6672416b..283fd9c35d6 100644 --- a/tensorflow/tools/api/golden/v1/tensorflow.linalg.pbtxt +++ b/tensorflow/tools/api/golden/v1/tensorflow.linalg.pbtxt @@ -108,18 +108,10 @@ tf_module { name: "diag_part" argspec: "args=[\'input\', \'name\', \'k\', \'padding_value\'], varargs=None, keywords=None, defaults=[\'diag_part\', \'0\', \'0\'], " } - member_method { - name: "eig" - argspec: "args=[\'tensor\', \'name\'], varargs=None, keywords=None, defaults=[\'None\'], " - } member_method { name: "eigh" argspec: "args=[\'tensor\', \'name\'], varargs=None, keywords=None, defaults=[\'None\'], " } - member_method { - name: "eigvals" - argspec: "args=[\'tensor\', \'name\'], varargs=None, keywords=None, defaults=[\'None\'], " - } member_method { name: "eigvalsh" argspec: "args=[\'tensor\', \'name\'], varargs=None, keywords=None, defaults=[\'None\'], " diff --git a/tensorflow/tools/api/golden/v1/tensorflow.raw_ops.pbtxt b/tensorflow/tools/api/golden/v1/tensorflow.raw_ops.pbtxt index 79e4810c7f4..8ae11431a08 100644 --- a/tensorflow/tools/api/golden/v1/tensorflow.raw_ops.pbtxt +++ b/tensorflow/tools/api/golden/v1/tensorflow.raw_ops.pbtxt @@ -1132,6 +1132,10 @@ tf_module { name: "EditDistance" argspec: "args=[\'hypothesis_indices\', \'hypothesis_values\', \'hypothesis_shape\', \'truth_indices\', \'truth_values\', \'truth_shape\', \'normalize\', \'name\'], varargs=None, keywords=None, defaults=[\'True\', \'None\'], " } + member_method { + name: "Eig" + argspec: "args=[\'input\', \'Tout\', \'compute_v\', \'name\'], varargs=None, keywords=None, defaults=[\'True\', \'None\'], " + } member_method { name: "Einsum" argspec: "args=[\'inputs\', \'equation\', \'name\'], varargs=None, keywords=None, defaults=[\'None\'], " @@ -3656,10 +3660,6 @@ tf_module { name: "SelfAdjointEig" argspec: "args=[\'input\', \'name\'], varargs=None, keywords=None, defaults=[\'None\'], " } - member_method { - name: "Eig" - argspec: "args=[\'input\', \'Tout\', \'compute_v\', \'name\'], varargs=None, keywords=None, defaults=[\'True\', \'None\'], " - } member_method { name: "SelfAdjointEigV2" argspec: "args=[\'input\', \'compute_v\', \'name\'], varargs=None, keywords=None, defaults=[\'True\', \'None\'], " diff --git a/tensorflow/tools/api/golden/v2/tensorflow.pbtxt b/tensorflow/tools/api/golden/v2/tensorflow.pbtxt index 06c52079cfd..9b3ba30c2ad 100644 --- a/tensorflow/tools/api/golden/v2/tensorflow.pbtxt +++ b/tensorflow/tools/api/golden/v2/tensorflow.pbtxt @@ -604,6 +604,14 @@ tf_module { name: "edit_distance" argspec: "args=[\'hypothesis\', \'truth\', \'normalize\', \'name\'], varargs=None, keywords=None, defaults=[\'True\', \'edit_distance\'], " } + member_method { + name: "eig" + argspec: "args=[\'tensor\', \'name\'], varargs=None, keywords=None, defaults=[\'None\'], " + } + member_method { + name: "eigvals" + argspec: "args=[\'tensor\', \'name\'], varargs=None, keywords=None, defaults=[\'None\'], " + } member_method { name: "einsum" argspec: "args=[\'equation\'], varargs=inputs, keywords=kwargs, defaults=None" diff --git a/tensorflow/tools/api/golden/v2/tensorflow.raw_ops.pbtxt b/tensorflow/tools/api/golden/v2/tensorflow.raw_ops.pbtxt index 79e4810c7f4..8ae11431a08 100644 --- a/tensorflow/tools/api/golden/v2/tensorflow.raw_ops.pbtxt +++ b/tensorflow/tools/api/golden/v2/tensorflow.raw_ops.pbtxt @@ -1132,6 +1132,10 @@ tf_module { name: "EditDistance" argspec: "args=[\'hypothesis_indices\', \'hypothesis_values\', \'hypothesis_shape\', \'truth_indices\', \'truth_values\', \'truth_shape\', \'normalize\', \'name\'], varargs=None, keywords=None, defaults=[\'True\', \'None\'], " } + member_method { + name: "Eig" + argspec: "args=[\'input\', \'Tout\', \'compute_v\', \'name\'], varargs=None, keywords=None, defaults=[\'True\', \'None\'], " + } member_method { name: "Einsum" argspec: "args=[\'inputs\', \'equation\', \'name\'], varargs=None, keywords=None, defaults=[\'None\'], " @@ -3656,10 +3660,6 @@ tf_module { name: "SelfAdjointEig" argspec: "args=[\'input\', \'name\'], varargs=None, keywords=None, defaults=[\'None\'], " } - member_method { - name: "Eig" - argspec: "args=[\'input\', \'Tout\', \'compute_v\', \'name\'], varargs=None, keywords=None, defaults=[\'True\', \'None\'], " - } member_method { name: "SelfAdjointEigV2" argspec: "args=[\'input\', \'compute_v\', \'name\'], varargs=None, keywords=None, defaults=[\'True\', \'None\'], " From a973c584a67ef3ede86eb5ee79772d1f7c6ef47a Mon Sep 17 00:00:00 2001 From: Evgenii Zheltonozhskii Date: Fri, 11 Oct 2019 21:37:29 +0300 Subject: [PATCH 10/11] Update ops --- tensorflow/core/ops/ops.pbtxt | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/tensorflow/core/ops/ops.pbtxt b/tensorflow/core/ops/ops.pbtxt index 00970ca44e8..1da4cef1557 100644 --- a/tensorflow/core/ops/ops.pbtxt +++ b/tensorflow/core/ops/ops.pbtxt @@ -12230,6 +12230,18 @@ op { attr { name: "T" type: "type" + allowed_values { + list { + type: DT_FLOAT + type: DT_DOUBLE + type: DT_COMPLEX64 + type: DT_COMPLEX128 + } + } + } + attr { + name: "Tout" + type: "type" allowed_values { list { type: DT_COMPLEX64 From cf27a56bbf2f95233df2655734015b50c1e56ecd Mon Sep 17 00:00:00 2001 From: Evgenii Zheltonozhskii Date: Fri, 11 Oct 2019 22:02:52 +0300 Subject: [PATCH 11/11] API definition update --- .../core/api_def/base_api/api_def_Eig.pbtxt | 45 +++++++++++++++++++ 1 file changed, 45 insertions(+) create mode 100644 tensorflow/core/api_def/base_api/api_def_Eig.pbtxt diff --git a/tensorflow/core/api_def/base_api/api_def_Eig.pbtxt b/tensorflow/core/api_def/base_api/api_def_Eig.pbtxt new file mode 100644 index 00000000000..b85082c0cc8 --- /dev/null +++ b/tensorflow/core/api_def/base_api/api_def_Eig.pbtxt @@ -0,0 +1,45 @@ +op { + graph_op_name: "Eig" + endpoint { + name: "Eig" + } + in_arg { + name: "input" + description: <